mirror of
https://github.com/graphql-python/graphene.git
synced 2025-07-18 12:02:19 +03:00
Merge branch 'graphql-python:master' into fix/inputobjecttype_undefined
This commit is contained in:
commit
a8cb8ca4c2
|
@ -1,12 +1,6 @@
|
||||||
Graphene
|
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:
|
Contents:
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
|
@ -271,7 +271,7 @@ The following is an example for creating a DateTime scalar:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_literal(node, _variables=None):
|
def parse_literal(node, _variables=None):
|
||||||
if isinstance(node, ast.StringValue):
|
if isinstance(node, ast.StringValueNode):
|
||||||
return datetime.datetime.strptime(
|
return datetime.datetime.strptime(
|
||||||
node.value, "%Y-%m-%dT%H:%M:%S.%f")
|
node.value, "%Y-%m-%dT%H:%M:%S.%f")
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ from .types import (
|
||||||
from .utils.module_loading import lazy_import
|
from .utils.module_loading import lazy_import
|
||||||
from .utils.resolve_only_args import resolve_only_args
|
from .utils.resolve_only_args import resolve_only_args
|
||||||
|
|
||||||
VERSION = (3, 1, 1, "final", 0)
|
VERSION = (3, 2, 1, "final", 0)
|
||||||
|
|
||||||
|
|
||||||
__version__ = get_version(VERSION)
|
__version__ = get_version(VERSION)
|
||||||
|
|
|
@ -31,18 +31,22 @@ class Argument(MountedType):
|
||||||
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
|
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
|
||||||
unmounted graphene type (ex. scalar or object) which is used for the type of this
|
unmounted graphene type (ex. scalar or object) which is used for the type of this
|
||||||
argument in the GraphQL schema.
|
argument in the GraphQL schema.
|
||||||
required (bool): indicates this argument as not null in the graphql schema. Same behavior
|
required (optional, bool): indicates this argument as not null in the graphql schema. Same behavior
|
||||||
as graphene.NonNull. Default False.
|
as graphene.NonNull. Default False.
|
||||||
name (str): the name of the GraphQL argument. Defaults to parameter name.
|
name (optional, str): the name of the GraphQL argument. Defaults to parameter name.
|
||||||
description (str): the description of the GraphQL argument in the schema.
|
description (optional, str): the description of the GraphQL argument in the schema.
|
||||||
default_value (Any): The value to be provided if the user does not set this argument in
|
default_value (optional, Any): The value to be provided if the user does not set this argument in
|
||||||
the operation.
|
the operation.
|
||||||
|
deprecation_reason (optional, str): Setting this value indicates that the argument is
|
||||||
|
depreciated and may provide instruction or reason on how for clients to proceed. Cannot be
|
||||||
|
set if the argument is required (see spec).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
type_,
|
type_,
|
||||||
default_value=Undefined,
|
default_value=Undefined,
|
||||||
|
deprecation_reason=None,
|
||||||
description=None,
|
description=None,
|
||||||
name=None,
|
name=None,
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -51,12 +55,16 @@ class Argument(MountedType):
|
||||||
super(Argument, self).__init__(_creation_counter=_creation_counter)
|
super(Argument, self).__init__(_creation_counter=_creation_counter)
|
||||||
|
|
||||||
if required:
|
if required:
|
||||||
|
assert (
|
||||||
|
deprecation_reason is None
|
||||||
|
), f"Argument {name} is required, cannot deprecate it."
|
||||||
type_ = NonNull(type_)
|
type_ = NonNull(type_)
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self._type = type_
|
self._type = type_
|
||||||
self.default_value = default_value
|
self.default_value = default_value
|
||||||
self.description = description
|
self.description = description
|
||||||
|
self.deprecation_reason = deprecation_reason
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
|
@ -68,6 +76,7 @@ class Argument(MountedType):
|
||||||
and self.type == other.type
|
and self.type == other.type
|
||||||
and self.default_value == other.default_value
|
and self.default_value == other.default_value
|
||||||
and self.description == other.description
|
and self.description == other.description
|
||||||
|
and self.deprecation_reason == other.deprecation_reason
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,14 @@ class InputField(MountedType):
|
||||||
description=None,
|
description=None,
|
||||||
required=False,
|
required=False,
|
||||||
_creation_counter=None,
|
_creation_counter=None,
|
||||||
**extra_args
|
**extra_args,
|
||||||
):
|
):
|
||||||
super(InputField, self).__init__(_creation_counter=_creation_counter)
|
super(InputField, self).__init__(_creation_counter=_creation_counter)
|
||||||
self.name = name
|
self.name = name
|
||||||
if required:
|
if required:
|
||||||
|
assert (
|
||||||
|
deprecation_reason is None
|
||||||
|
), f"InputField {name} is required, cannot deprecate it."
|
||||||
type_ = NonNull(type_)
|
type_ = NonNull(type_)
|
||||||
self._type = type_
|
self._type = type_
|
||||||
self.deprecation_reason = deprecation_reason
|
self.deprecation_reason = deprecation_reason
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from enum import Enum as PyEnum
|
||||||
import inspect
|
import inspect
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
@ -169,10 +170,16 @@ class TypeMap(dict):
|
||||||
values = {}
|
values = {}
|
||||||
for name, value in graphene_type._meta.enum.__members__.items():
|
for name, value in graphene_type._meta.enum.__members__.items():
|
||||||
description = getattr(value, "description", None)
|
description = getattr(value, "description", None)
|
||||||
deprecation_reason = getattr(value, "deprecation_reason", None)
|
# if the "description" attribute is an Enum, it is likely an enum member
|
||||||
|
# called description, not a description property
|
||||||
|
if isinstance(description, PyEnum):
|
||||||
|
description = None
|
||||||
if not description and callable(graphene_type._meta.description):
|
if not description and callable(graphene_type._meta.description):
|
||||||
description = graphene_type._meta.description(value)
|
description = graphene_type._meta.description(value)
|
||||||
|
|
||||||
|
deprecation_reason = getattr(value, "deprecation_reason", None)
|
||||||
|
if isinstance(deprecation_reason, PyEnum):
|
||||||
|
deprecation_reason = None
|
||||||
if not deprecation_reason and callable(
|
if not deprecation_reason and callable(
|
||||||
graphene_type._meta.deprecation_reason
|
graphene_type._meta.deprecation_reason
|
||||||
):
|
):
|
||||||
|
@ -309,6 +316,7 @@ class TypeMap(dict):
|
||||||
default_value=field.default_value,
|
default_value=field.default_value,
|
||||||
out_name=name,
|
out_name=name,
|
||||||
description=field.description,
|
description=field.description,
|
||||||
|
deprecation_reason=field.deprecation_reason,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
args = {}
|
args = {}
|
||||||
|
@ -320,6 +328,7 @@ class TypeMap(dict):
|
||||||
out_name=arg_name,
|
out_name=arg_name,
|
||||||
description=arg.description,
|
description=arg.description,
|
||||||
default_value=arg.default_value,
|
default_value=arg.default_value,
|
||||||
|
deprecation_reason=arg.deprecation_reason,
|
||||||
)
|
)
|
||||||
subscribe = field.wrap_subscribe(
|
subscribe = field.wrap_subscribe(
|
||||||
self.get_function_for_type(
|
self.get_function_for_type(
|
||||||
|
|
|
@ -18,8 +18,20 @@ def test_argument():
|
||||||
|
|
||||||
|
|
||||||
def test_argument_comparasion():
|
def test_argument_comparasion():
|
||||||
arg1 = Argument(String, name="Hey", description="Desc", default_value="default")
|
arg1 = Argument(
|
||||||
arg2 = Argument(String, name="Hey", description="Desc", default_value="default")
|
String,
|
||||||
|
name="Hey",
|
||||||
|
description="Desc",
|
||||||
|
default_value="default",
|
||||||
|
deprecation_reason="deprecated",
|
||||||
|
)
|
||||||
|
arg2 = Argument(
|
||||||
|
String,
|
||||||
|
name="Hey",
|
||||||
|
description="Desc",
|
||||||
|
default_value="default",
|
||||||
|
deprecation_reason="deprecated",
|
||||||
|
)
|
||||||
|
|
||||||
assert arg1 == arg2
|
assert arg1 == arg2
|
||||||
assert arg1 != String()
|
assert arg1 != String()
|
||||||
|
@ -40,6 +52,30 @@ def test_to_arguments():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_arguments_deprecated():
|
||||||
|
args = {"unmounted_arg": String(required=False, deprecation_reason="deprecated")}
|
||||||
|
|
||||||
|
my_args = to_arguments(args)
|
||||||
|
assert my_args == {
|
||||||
|
"unmounted_arg": Argument(
|
||||||
|
String, required=False, deprecation_reason="deprecated"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_arguments_required_deprecated():
|
||||||
|
args = {
|
||||||
|
"unmounted_arg": String(
|
||||||
|
required=True, name="arg", deprecation_reason="deprecated"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
with raises(AssertionError) as exc_info:
|
||||||
|
to_arguments(args)
|
||||||
|
|
||||||
|
assert str(exc_info.value) == "Argument arg is required, cannot deprecate it."
|
||||||
|
|
||||||
|
|
||||||
def test_to_arguments_raises_if_field():
|
def test_to_arguments_raises_if_field():
|
||||||
args = {"arg_string": Field(String)}
|
args = {"arg_string": Field(String)}
|
||||||
|
|
||||||
|
|
|
@ -565,3 +565,36 @@ def test_iterable_instance_creation_enum():
|
||||||
for c in TestEnum:
|
for c in TestEnum:
|
||||||
result.append(c.name)
|
result.append(c.name)
|
||||||
assert result == expected_values
|
assert result == expected_values
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/graphql-python/graphene/issues/1321
|
||||||
|
def test_enum_description_member_not_interpreted_as_property():
|
||||||
|
class RGB(Enum):
|
||||||
|
"""Description"""
|
||||||
|
|
||||||
|
red = "red"
|
||||||
|
green = "green"
|
||||||
|
blue = "blue"
|
||||||
|
description = "description"
|
||||||
|
deprecation_reason = "deprecation_reason"
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
color = RGB()
|
||||||
|
|
||||||
|
def resolve_color(_, info):
|
||||||
|
return RGB.description
|
||||||
|
|
||||||
|
values = RGB._meta.enum.__members__.values()
|
||||||
|
assert sorted(v.name for v in values) == [
|
||||||
|
"blue",
|
||||||
|
"deprecation_reason",
|
||||||
|
"description",
|
||||||
|
"green",
|
||||||
|
"red",
|
||||||
|
]
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
results = schema.execute("query { color }")
|
||||||
|
assert not results.errors
|
||||||
|
assert results.data["color"] == RGB.description.name
|
||||||
|
|
|
@ -128,13 +128,20 @@ def test_field_name_as_argument():
|
||||||
|
|
||||||
def test_field_source_argument_as_kw():
|
def test_field_source_argument_as_kw():
|
||||||
MyType = object()
|
MyType = object()
|
||||||
field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False))
|
deprecation_reason = "deprecated"
|
||||||
|
field = Field(
|
||||||
|
MyType,
|
||||||
|
b=NonNull(True),
|
||||||
|
c=Argument(None, deprecation_reason=deprecation_reason),
|
||||||
|
a=NonNull(False),
|
||||||
|
)
|
||||||
assert list(field.args) == ["b", "c", "a"]
|
assert list(field.args) == ["b", "c", "a"]
|
||||||
assert isinstance(field.args["b"], Argument)
|
assert isinstance(field.args["b"], Argument)
|
||||||
assert isinstance(field.args["b"].type, NonNull)
|
assert isinstance(field.args["b"].type, NonNull)
|
||||||
assert field.args["b"].type.of_type is True
|
assert field.args["b"].type.of_type is True
|
||||||
assert isinstance(field.args["c"], Argument)
|
assert isinstance(field.args["c"], Argument)
|
||||||
assert field.args["c"].type is None
|
assert field.args["c"].type is None
|
||||||
|
assert field.args["c"].deprecation_reason == deprecation_reason
|
||||||
assert isinstance(field.args["a"], Argument)
|
assert isinstance(field.args["a"], Argument)
|
||||||
assert isinstance(field.args["a"].type, NonNull)
|
assert isinstance(field.args["a"].type, NonNull)
|
||||||
assert field.args["a"].type.of_type is False
|
assert field.args["a"].type.of_type is False
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
from pytest import raises
|
||||||
|
|
||||||
from ..inputfield import InputField
|
from ..inputfield import InputField
|
||||||
from ..structures import NonNull
|
from ..structures import NonNull
|
||||||
from .utils import MyLazyType
|
from .utils import MyLazyType
|
||||||
|
@ -12,6 +14,22 @@ def test_inputfield_required():
|
||||||
assert field.type.of_type == MyType
|
assert field.type.of_type == MyType
|
||||||
|
|
||||||
|
|
||||||
|
def test_inputfield_deprecated():
|
||||||
|
MyType = object()
|
||||||
|
deprecation_reason = "deprecated"
|
||||||
|
field = InputField(MyType, required=False, deprecation_reason=deprecation_reason)
|
||||||
|
assert isinstance(field.type, type(MyType))
|
||||||
|
assert field.deprecation_reason == deprecation_reason
|
||||||
|
|
||||||
|
|
||||||
|
def test_inputfield_required_deprecated():
|
||||||
|
MyType = object()
|
||||||
|
with raises(AssertionError) as exc_info:
|
||||||
|
InputField(MyType, name="input", required=True, deprecation_reason="deprecated")
|
||||||
|
|
||||||
|
assert str(exc_info.value) == "InputField input is required, cannot deprecate it."
|
||||||
|
|
||||||
|
|
||||||
def test_inputfield_with_lazy_type():
|
def test_inputfield_with_lazy_type():
|
||||||
MyType = object()
|
MyType = object()
|
||||||
field = InputField(lambda: MyType)
|
field = InputField(lambda: MyType)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user