Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding node_tree method in utilities to debug and compare DocumentNode instances #449

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
Loading