-
-
Notifications
You must be signed in to change notification settings - Fork 509
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for printing dictionaries when using custom scalars
- Loading branch information
Showing
8 changed files
with
231 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
Release type: minor | ||
|
||
This release adds support for printing default values for scalars like JSON. | ||
|
||
For example the following: | ||
|
||
```python | ||
import strawberry | ||
from strawberry.scalars import JSON | ||
|
||
|
||
@strawberry.input | ||
class MyInput: | ||
j: JSON = strawberry.field(default_factory=dict) | ||
j2: JSON = strawberry.field(default_factory=lambda: {"hello": "world"}) | ||
``` | ||
|
||
will print the following schema: | ||
|
||
```graphql | ||
input MyInput { | ||
j: JSON! = {} | ||
j2: JSON! = {hello: "world"} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .printer import print_schema | ||
|
||
|
||
__all__ = ["print_schema"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import re | ||
from math import isfinite | ||
from typing import Any, Mapping, Optional, cast | ||
|
||
from graphql.language import ( | ||
BooleanValueNode, | ||
EnumValueNode, | ||
FloatValueNode, | ||
IntValueNode, | ||
ListValueNode, | ||
NameNode, | ||
NullValueNode, | ||
ObjectFieldNode, | ||
ObjectValueNode, | ||
StringValueNode, | ||
ValueNode, | ||
) | ||
from graphql.pyutils import Undefined, inspect, is_iterable | ||
from graphql.type import ( | ||
GraphQLID, | ||
GraphQLInputObjectType, | ||
GraphQLInputType, | ||
GraphQLList, | ||
GraphQLNonNull, | ||
is_enum_type, | ||
is_input_object_type, | ||
is_leaf_type, | ||
is_list_type, | ||
is_non_null_type, | ||
) | ||
|
||
|
||
__all__ = ["ast_from_value"] | ||
|
||
_re_integer_string = re.compile("^-?(?:0|[1-9][0-9]*)$") | ||
|
||
|
||
def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]: | ||
# custom ast_from_value that allows to also serialize custom scalar that aren't | ||
# basic types, namely JSON scalar types | ||
|
||
if is_non_null_type(type_): | ||
type_ = cast(GraphQLNonNull, type_) | ||
ast_value = ast_from_value(value, type_.of_type) | ||
if isinstance(ast_value, NullValueNode): | ||
return None | ||
return ast_value | ||
|
||
# only explicit None, not Undefined or NaN | ||
if value is None: | ||
return NullValueNode() | ||
|
||
# undefined | ||
if value is Undefined: | ||
return None | ||
|
||
# Convert Python list to GraphQL list. If the GraphQLType is a list, but the value | ||
# is not a list, convert the value using the list's item type. | ||
if is_list_type(type_): | ||
type_ = cast(GraphQLList, type_) | ||
item_type = type_.of_type | ||
if is_iterable(value): | ||
maybe_value_nodes = (ast_from_value(item, item_type) for item in value) | ||
value_nodes = tuple(node for node in maybe_value_nodes if node) | ||
return ListValueNode(values=value_nodes) | ||
return ast_from_value(value, item_type) | ||
|
||
# Populate the fields of the input object by creating ASTs from each value in the | ||
# Python dict according to the fields in the input type. | ||
if is_input_object_type(type_): | ||
if value is None or not isinstance(value, Mapping): | ||
return None | ||
type_ = cast(GraphQLInputObjectType, type_) | ||
field_items = ( | ||
(field_name, ast_from_value(value[field_name], field.type)) | ||
for field_name, field in type_.fields.items() | ||
if field_name in value | ||
) | ||
field_nodes = tuple( | ||
ObjectFieldNode(name=NameNode(value=field_name), value=field_value) | ||
for field_name, field_value in field_items | ||
if field_value | ||
) | ||
return ObjectValueNode(fields=field_nodes) | ||
|
||
if is_leaf_type(type_): | ||
# Since value is an internally represented value, it must be serialized to an | ||
# externally represented value before converting into an AST. | ||
serialized = type_.serialize(value) # type: ignore | ||
if serialized is None or serialized is Undefined: | ||
return None | ||
|
||
# Others serialize based on their corresponding Python scalar types. | ||
if isinstance(serialized, bool): | ||
return BooleanValueNode(value=serialized) | ||
|
||
# Python ints and floats correspond nicely to Int and Float values. | ||
if isinstance(serialized, int): | ||
return IntValueNode(value=str(serialized)) | ||
if isinstance(serialized, float) and isfinite(serialized): | ||
value = str(serialized) | ||
if value.endswith(".0"): | ||
value = value[:-2] | ||
return FloatValueNode(value=value) | ||
|
||
if isinstance(serialized, str): | ||
# Enum types use Enum literals. | ||
if is_enum_type(type_): | ||
return EnumValueNode(value=serialized) | ||
|
||
# ID types can use Int literals. | ||
if type_ is GraphQLID and _re_integer_string.match(serialized): | ||
return IntValueNode(value=serialized) | ||
|
||
return StringValueNode(value=serialized) | ||
|
||
if isinstance(serialized, dict): | ||
return ObjectValueNode( | ||
fields=[ | ||
ObjectFieldNode( | ||
name=NameNode(value=key), | ||
value=StringValueNode(value=value), | ||
) | ||
for key, value in serialized.items() | ||
] | ||
) | ||
|
||
breakpoint() | ||
|
||
raise TypeError(f"Cannot convert value to AST: {inspect(serialized)}.") | ||
|
||
# Not reachable. All possible input types have been considered. | ||
raise TypeError(f"Unexpected input type: {inspect(type_)}.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters