Skip to content

Commit 19ea63b

Browse files
authored
fix: Input fields and Arguments can now be deprecated (#1472)
Non-required InputFields and arguments now support deprecation via setting the `deprecation_reason` argument upon creation.
1 parent d5dadb7 commit 19ea63b

File tree

6 files changed

+83
-8
lines changed

6 files changed

+83
-8
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/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/schema.py

+2
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
316316
default_value=field.default_value,
317317
out_name=name,
318318
description=field.description,
319+
deprecation_reason=field.deprecation_reason,
319320
)
320321
else:
321322
args = {}
@@ -327,6 +328,7 @@ def create_fields_for_type(self, graphene_type, is_input_type=False):
327328
out_name=arg_name,
328329
description=arg.description,
329330
default_value=arg.default_value,
331+
deprecation_reason=arg.deprecation_reason,
330332
)
331333
subscribe = field.wrap_subscribe(
332334
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

+8-1
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,20 @@ def test_field_name_as_argument():
128128

129129
def test_field_source_argument_as_kw():
130130
MyType = object()
131-
field = Field(MyType, b=NonNull(True), c=Argument(None), a=NonNull(False))
131+
deprecation_reason = "deprecated"
132+
field = Field(
133+
MyType,
134+
b=NonNull(True),
135+
c=Argument(None, deprecation_reason=deprecation_reason),
136+
a=NonNull(False),
137+
)
132138
assert list(field.args) == ["b", "c", "a"]
133139
assert isinstance(field.args["b"], Argument)
134140
assert isinstance(field.args["b"].type, NonNull)
135141
assert field.args["b"].type.of_type is True
136142
assert isinstance(field.args["c"], Argument)
137143
assert field.args["c"].type is None
144+
assert field.args["c"].deprecation_reason == deprecation_reason
138145
assert isinstance(field.args["a"], Argument)
139146
assert isinstance(field.args["a"].type, NonNull)
140147
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)

0 commit comments

Comments
 (0)