Skip to content

Commit

Permalink
Adding node_tree method in utilities to debug and compare DocumentNod…
Browse files Browse the repository at this point in the history
…e instances (#449)

DSL: Set variable_definitions to None for Fragments by default instead of empty tuple
  • Loading branch information
leszekhanusz authored Nov 21, 2023
1 parent 3a48a2f commit a2f327f
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 4 deletions.
18 changes: 17 additions & 1 deletion gql/dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,10 +1068,26 @@ def executable_ast(self) -> FragmentDefinitionNode:
"Missing type condition. Please use .on(type_condition) method"
)

fragment_variable_definitions = self.variable_definitions.get_ast_definitions()

if len(fragment_variable_definitions) == 0:
"""Fragment variable definitions are obsolete and only supported on
graphql-core if the Parser is initialized with:
allow_legacy_fragment_variables=True.
We will not provide variable_definitions instead of providing an empty
tuple to be coherent with how it works by default on graphql-core.
"""
variable_definition_kwargs = {}
else:
variable_definition_kwargs = {
"variable_definitions": fragment_variable_definitions
}

return FragmentDefinitionNode(
type_condition=NamedTypeNode(name=NameNode(value=self._type.name)),
selection_set=self.selection_set,
variable_definitions=self.variable_definitions.get_ast_definitions(),
**variable_definition_kwargs,
name=NameNode(value=self.name),
directives=(),
)
Expand Down
2 changes: 2 additions & 0 deletions gql/utilities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from .build_client_schema import build_client_schema
from .get_introspection_query_ast import get_introspection_query_ast
from .node_tree import node_tree
from .parse_result import parse_result
from .serialize_variable_values import serialize_value, serialize_variable_values
from .update_schema_enum import update_schema_enum
from .update_schema_scalars import update_schema_scalar, update_schema_scalars

__all__ = [
"build_client_schema",
"node_tree",
"parse_result",
"get_introspection_query_ast",
"serialize_variable_values",
Expand Down
89 changes: 89 additions & 0 deletions gql/utilities/node_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from typing import Any, Iterable, List, Optional, Sized

from graphql import Node


def _node_tree_recursive(
obj: Any,
*,
indent: int = 0,
ignored_keys: List,
):

assert ignored_keys is not None

results = []

if hasattr(obj, "__slots__"):

results.append(" " * indent + f"{type(obj).__name__}")

try:
keys = obj.keys
except AttributeError:
# If the object has no keys attribute, print its repr and return.
results.append(" " * (indent + 1) + repr(obj))
else:
for key in keys:
if key in ignored_keys:
continue
attr_value = getattr(obj, key, None)
results.append(" " * (indent + 1) + f"{key}:")
if isinstance(attr_value, Iterable) and not isinstance(
attr_value, (str, bytes)
):
if isinstance(attr_value, Sized) and len(attr_value) == 0:
results.append(
" " * (indent + 2) + f"empty {type(attr_value).__name__}"
)
else:
for item in attr_value:
results.append(
_node_tree_recursive(
item,
indent=indent + 2,
ignored_keys=ignored_keys,
)
)
else:
results.append(
_node_tree_recursive(
attr_value,
indent=indent + 2,
ignored_keys=ignored_keys,
)
)
else:
results.append(" " * indent + repr(obj))

return "\n".join(results)


def node_tree(
obj: Node,
*,
ignore_loc: bool = True,
ignore_block: bool = True,
ignored_keys: Optional[List] = None,
):
"""Method which returns a tree of Node elements as a String.
Useful to debug deep DocumentNode instances created by gql or dsl_gql.
WARNING: the output of this method is not guaranteed and may change without notice.
"""

assert isinstance(obj, Node)

if ignored_keys is None:
ignored_keys = []

if ignore_loc:
# We are ignoring loc attributes by default
ignored_keys.append("loc")

if ignore_block:
# We are ignoring block attributes by default (in StringValueNode)
ignored_keys.append("block")

return _node_tree_recursive(obj, ignored_keys=ignored_keys)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from gql import Client
from gql.dsl import DSLFragment, DSLQuery, DSLSchema, dsl_gql
from gql import Client, gql
from gql.dsl import DSLFragment, DSLQuery, DSLSchema, dsl_gql, print_ast
from gql.utilities import node_tree

schema_str = """
type MonsterForm {
Expand Down Expand Up @@ -57,3 +58,17 @@ def test_issue_447():
q = dsl_gql(sprite, copy_of, DSLQuery(query))

client.validate(q)

# Creating a tree from the DocumentNode created by dsl_gql
dsl_tree = node_tree(q)

# Creating a tree from the DocumentNode created by gql
gql_tree = node_tree(gql(print_ast(q)))

print("=======")
print(dsl_tree)
print("+++++++")
print(gql_tree)
print("=======")

assert dsl_tree == gql_tree
Loading

0 comments on commit a2f327f

Please sign in to comment.