Skip to content

Commit f6fc761

Browse files
committed
Pass deprecation reason to graphql core schema construction
1 parent ccdd35b commit f6fc761

11 files changed

+108
-12
lines changed

graphene/types/argument.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,22 @@ class Argument(MountedType):
3131
type (class for a graphene.UnmountedType): must be a class (not an instance) of an
3232
unmounted graphene type (ex. scalar or object) which is used for the type of this
3333
argument in the GraphQL schema.
34-
required (bool): indicates this argument as not null in the graphql schema. Same behavior
34+
required (optional, bool): indicates this argument as not null in the graphql schema. Same behavior
3535
as graphene.NonNull. Default False.
36-
name (str): the name of the GraphQL argument. Defaults to parameter name.
37-
description (str): the description of the GraphQL argument in the schema.
38-
default_value (Any): The value to be provided if the user does not set this argument in
36+
name (optional, str): the name of the GraphQL argument. Defaults to parameter name.
37+
description (optional, str): the description of the GraphQL argument in the schema.
38+
default_value (optional, Any): The value to be provided if the user does not set this argument in
3939
the operation.
40+
deprecation_reason (optional, str): Setting this value indicates that the argument is
41+
depreciated and may provide instruction or reason on how for clients to proceed. Cannot be
42+
set if the argument is required (see spec).
4043
"""
4144

4245
def __init__(
4346
self,
4447
type_,
4548
default_value=Undefined,
49+
deprecation_reason=None,
4650
description=None,
4751
name=None,
4852
required=False,
@@ -51,12 +55,16 @@ def __init__(
5155
super(Argument, self).__init__(_creation_counter=_creation_counter)
5256

5357
if required:
58+
assert (
59+
deprecation_reason is None
60+
), f"Argument {name} is required, cannot deprecate it."
5461
type_ = NonNull(type_)
5562

5663
self.name = name
5764
self._type = type_
5865
self.default_value = default_value
5966
self.description = description
67+
self.deprecation_reason = deprecation_reason
6068

6169
@property
6270
def type(self):
@@ -68,6 +76,7 @@ def __eq__(self, other):
6876
and self.type == other.type
6977
and self.default_value == other.default_value
7078
and self.description == other.description
79+
and self.deprecation_reason == other.deprecation_reason
7180
)
7281

7382

graphene/types/field.py

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ def __init__(
8989
), f'The default value can not be a function but received "{base_type(default_value)}".'
9090

9191
if required:
92+
assert (
93+
deprecation_reason is None
94+
), f"Field {name} is required, cannot deprecate it."
9295
type_ = NonNull(type_)
9396

9497
# Check if name is actually an argument of the field

graphene/types/inputfield.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,14 @@ def __init__(
5555
description=None,
5656
required=False,
5757
_creation_counter=None,
58-
**extra_args
58+
**extra_args,
5959
):
6060
super(InputField, self).__init__(_creation_counter=_creation_counter)
6161
self.name = name
6262
if required:
63+
assert (
64+
deprecation_reason is None
65+
), f"InputField {name} is required, cannot deprecate it."
6366
type_ = NonNull(type_)
6467
self._type = type_
6568
self.deprecation_reason = deprecation_reason

graphene/types/mountedtype.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def mounted(cls, unmounted): # noqa: N802
88
"""
99
Mount the UnmountedType instance
1010
"""
11+
1112
assert isinstance(
1213
unmounted, UnmountedType
1314
), f"{cls.__name__} can't mount {repr(unmounted)}"

graphene/types/schema.py

+2
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
309309
default_value=field.default_value,
310310
out_name=name,
311311
description=field.description,
312+
deprecation_reason=field.deprecation_reason,
312313
)
313314
else:
314315
args = {}
@@ -320,6 +321,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
320321
out_name=arg_name,
321322
description=arg.description,
322323
default_value=arg.default_value,
324+
deprecation_reason=arg.deprecation_reason,
323325
)
324326
subscribe = field.wrap_subscribe(
325327
self.get_function_for_type(

graphene/types/tests/test_argument.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,20 @@ def test_argument():
1818

1919

2020
def test_argument_comparasion():
21-
arg1 = Argument(String, name="Hey", description="Desc", default_value="default")
22-
arg2 = Argument(String, name="Hey", description="Desc", default_value="default")
21+
arg1 = Argument(
22+
String,
23+
name="Hey",
24+
description="Desc",
25+
default_value="default",
26+
deprecation_reason="deprecated",
27+
)
28+
arg2 = Argument(
29+
String,
30+
name="Hey",
31+
description="Desc",
32+
default_value="default",
33+
deprecation_reason="deprecated",
34+
)
2335

2436
assert arg1 == arg2
2537
assert arg1 != String()
@@ -40,6 +52,30 @@ def test_to_arguments():
4052
}
4153

4254

55+
def test_to_arguments_deprecated():
56+
args = {"unmounted_arg": String(required=False, deprecation_reason="deprecated")}
57+
58+
my_args = to_arguments(args)
59+
assert my_args == {
60+
"unmounted_arg": Argument(
61+
String, required=False, deprecation_reason="deprecated"
62+
),
63+
}
64+
65+
66+
def test_to_arguments_required_deprecated():
67+
args = {
68+
"unmounted_arg": String(
69+
required=True, name="arg", deprecation_reason="deprecated"
70+
)
71+
}
72+
73+
with raises(AssertionError) as exc_info:
74+
to_arguments(args)
75+
76+
assert str(exc_info.value) == "Argument arg is required, cannot deprecate it."
77+
78+
4379
def test_to_arguments_raises_if_field():
4480
args = {"arg_string": Field(String)}
4581

graphene/types/tests/test_field.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ def test_field_required():
5151
assert field.type.of_type == MyType
5252

5353

54+
def test_field_required_deprecated():
55+
MyType = object()
56+
with raises(AssertionError) as exc_info:
57+
Field(MyType, name="field", required=True, deprecation_reason="deprecated")
58+
59+
assert str(exc_info.value) == "Field field is required, cannot deprecate it."
60+
61+
5462
def test_field_default_value_not_callable():
5563
MyType = object()
5664
try:
@@ -128,13 +136,20 @@ def test_field_name_as_argument():
128136

129137
def test_field_source_argument_as_kw():
130138
MyType = object()
131-
field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False))
139+
deprecation_reason = "deprecated"
140+
field = Field(
141+
MyType,
142+
b=NonNull(True),
143+
c=Argument(None, deprecation_reason=deprecation_reason),
144+
a=NonNull(False),
145+
)
132146
assert list(field.args) == ["b", "c", "a"]
133147
assert isinstance(field.args["b"], Argument)
134148
assert isinstance(field.args["b"].type, NonNull)
135149
assert field.args["b"].type.of_type is True
136150
assert isinstance(field.args["c"], Argument)
137151
assert field.args["c"].type is None
152+
assert field.args["c"].deprecation_reason == deprecation_reason
138153
assert isinstance(field.args["a"], Argument)
139154
assert isinstance(field.args["a"].type, NonNull)
140155
assert field.args["a"].type.of_type is False

graphene/types/tests/test_inputfield.py

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from functools import partial
22

3+
from pytest import raises
4+
35
from ..inputfield import InputField
46
from ..structures import NonNull
57
from .utils import MyLazyType
@@ -12,6 +14,22 @@ def test_inputfield_required():
1214
assert field.type.of_type == MyType
1315

1416

17+
def test_inputfield_deprecated():
18+
MyType = object()
19+
deprecation_reason = "deprecated"
20+
field = InputField(MyType, required=False, deprecation_reason=deprecation_reason)
21+
assert isinstance(field.type, type(MyType))
22+
assert field.deprecation_reason == deprecation_reason
23+
24+
25+
def test_inputfield_required_deprecated():
26+
MyType = object()
27+
with raises(AssertionError) as exc_info:
28+
InputField(MyType, name="input", required=True, deprecation_reason="deprecated")
29+
30+
assert str(exc_info.value) == "InputField input is required, cannot deprecate it."
31+
32+
1533
def test_inputfield_with_lazy_type():
1634
MyType = object()
1735
field = InputField(lambda: MyType)

graphene/types/tests/test_mutation.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from ..objecttype import ObjectType
77
from ..scalars import String
88
from ..schema import Schema
9-
from ..structures import NonNull
109
from ..interface import Interface
1110

1211

@@ -149,14 +148,14 @@ class MyMutation(ObjectType):
149148
name="createUser",
150149
description="Create a user",
151150
deprecation_reason="Is deprecated",
152-
required=True,
151+
required=False,
153152
)
154153

155154
field = MyMutation._meta.fields["create_user"]
156155
assert field.name == "createUser"
157156
assert field.description == "Create a user"
158157
assert field.deprecation_reason == "Is deprecated"
159-
assert field.type == NonNull(CreateUser)
158+
assert field.type == CreateUser
160159

161160

162161
def test_mutation_default_args_output():

graphene/types/unmountedtype.py

+10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class MyObjectType(ObjectType):
3535
3636
An unmounted type will accept arguments based upon its context (ObjectType, Field or
3737
InputObjectType) and pass it on to the appropriate MountedType (Field, Argument or InputField).
38+
Unmounted types which inherit SubclassWithMeta also pass some Meta values as arguments.
39+
Precendence is kwargs > meta_args.
3840
3941
See each Mounted type reference for more information about valid parameters.
4042
"""
@@ -85,3 +87,11 @@ def __eq__(self, other):
8587
and self.args == other.args
8688
and self.kwargs == other.kwargs
8789
)
90+
91+
@classmethod
92+
def mountable_meta(cls):
93+
"""
94+
Classes can use this method to select which meta fields
95+
can be inherited when mounting.
96+
"""
97+
return tuple()

graphene/validation/depth_limit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
def depth_limit_validator(
5454
max_depth: int,
5555
ignore: Optional[List[IgnoreType]] = None,
56-
callback: Callable[[Dict[str, int]], None] = None,
56+
callback: Optional[Callable[[Dict[str, int]], None]] = None,
5757
):
5858
class DepthLimitValidator(ValidationRule):
5959
def __init__(self, validation_context: ValidationContext):

0 commit comments

Comments
 (0)