Skip to content

Commit dceb5cd

Browse files
committed
Add support for adding description to schema
Replicates graphql/graphql-js@b883320
1 parent 67d28f8 commit dceb5cd

21 files changed

+147
-35
lines changed

src/graphql/language/ast.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,9 @@ class TypeSystemDefinitionNode(DefinitionNode):
477477

478478

479479
class SchemaDefinitionNode(TypeSystemDefinitionNode):
480-
__slots__ = "directives", "operation_types"
480+
__slots__ = "description", "directives", "operation_types"
481481

482+
description: Optional[StringValueNode]
482483
directives: Optional[FrozenList[DirectiveNode]]
483484
operation_types: FrozenList["OperationTypeDefinitionNode"]
484485

src/graphql/language/parser.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,13 +581,17 @@ def parse_description(self) -> Optional[StringValueNode]:
581581
def parse_schema_definition(self) -> SchemaDefinitionNode:
582582
"""SchemaDefinition"""
583583
start = self._lexer.token
584+
description = self.parse_description()
584585
self.expect_keyword("schema")
585586
directives = self.parse_directives(True)
586587
operation_types = self.many(
587588
TokenKind.BRACE_L, self.parse_operation_type_definition, TokenKind.BRACE_R
588589
)
589590
return SchemaDefinitionNode(
590-
directives=directives, operation_types=operation_types, loc=self.loc(start)
591+
description=description,
592+
directives=directives,
593+
operation_types=operation_types,
594+
loc=self.loc(start),
591595
)
592596

593597
def parse_operation_type_definition(self) -> OperationTypeDefinitionNode:

src/graphql/language/printer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ def leave_non_null_type(self, node, *_args):
153153

154154
# Type System Definitions
155155

156+
@add_description
156157
def leave_schema_definition(self, node, *_args):
157158
return join(
158159
["schema", join(node.directives, " "), block(node.operation_types)], " "

src/graphql/language/visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
"named_type": ("name",),
7272
"list_type": ("type",),
7373
"non_null_type": ("type",),
74-
"schema_definition": ("directives", "operation_types"),
74+
"schema_definition": ("description", "directives", "operation_types"),
7575
"operation_type_definition": ("type",),
7676
"scalar_type_definition": ("description", "name", "directives"),
7777
"object_type_definition": (

src/graphql/type/introspection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
" on the server, as well as the entry points for query,"
4141
" mutation, and subscription operations.",
4242
fields=lambda: {
43+
"description": GraphQLField(
44+
GraphQLString, resolve=lambda schema, _info: schema.description
45+
),
4346
"types": GraphQLField(
4447
GraphQLNonNull(GraphQLList(GraphQLNonNull(__Type))),
4548
resolve=lambda schema, _info: schema.type_map.values(),

src/graphql/type/schema.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from ..error import GraphQLError
1414
from ..language import ast
15-
from ..pyutils import inspect, is_collection, FrozenList
15+
from ..pyutils import inspect, is_collection, is_description, FrozenList
1616
from .definition import (
1717
GraphQLAbstractType,
1818
GraphQLInterfaceType,
@@ -94,6 +94,7 @@ class GraphQLSchema:
9494
subscription_type: Optional[GraphQLObjectType]
9595
type_map: TypeMap
9696
directives: FrozenList[GraphQLDirective]
97+
description: Optional[str]
9798
extensions: Optional[Dict[str, Any]]
9899
ast_node: Optional[ast.SchemaDefinitionNode]
99100
extension_ast_nodes: Optional[FrozenList[ast.SchemaExtensionNode]]
@@ -109,6 +110,7 @@ def __init__(
109110
subscription: Optional[GraphQLObjectType] = None,
110111
types: Optional[Collection[GraphQLNamedType]] = None,
111112
directives: Optional[Collection[GraphQLDirective]] = None,
113+
description: Optional[str] = None,
112114
extensions: Optional[Dict[str, Any]] = None,
113115
ast_node: Optional[ast.SchemaDefinitionNode] = None,
114116
extension_ast_nodes: Optional[Collection[ast.SchemaExtensionNode]] = None,
@@ -144,6 +146,8 @@ def __init__(
144146
raise TypeError("Schema directives must be a collection.")
145147
if not isinstance(directives, FrozenList):
146148
directives = FrozenList(directives)
149+
if description is not None and not is_description(description):
150+
raise TypeError("Schema description must be a string.")
147151
if extensions is not None and (
148152
not isinstance(extensions, dict)
149153
or not all(isinstance(key, str) for key in extensions)
@@ -163,6 +167,7 @@ def __init__(
163167
if not isinstance(extension_ast_nodes, FrozenList):
164168
extension_ast_nodes = FrozenList(extension_ast_nodes)
165169

170+
self.description = description
166171
self.extensions = extensions
167172
self.ast_node = ast_node
168173
self.extension_ast_nodes = (
@@ -268,6 +273,7 @@ def to_kwargs(self) -> Dict[str, Any]:
268273
subscription=self.subscription_type,
269274
types=FrozenList(self.type_map.values()) or None,
270275
directives=self.directives[:],
276+
description=self.description,
271277
extensions=self.extensions,
272278
ast_node=self.ast_node,
273279
extension_ast_nodes=self.extension_ast_nodes,

src/graphql/utilities/build_client_schema.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ def build_directive(directive_introspection: Dict) -> GraphQLDirective:
337337
type_map[std_type_name] = std_type
338338

339339
# Get the root Query, Mutation, and Subscription types.
340-
341340
query_type_ref = schema_introspection.get("queryType")
342341
query_type = None if query_type_ref is None else get_object_type(query_type_ref)
343342
mutation_type_ref = schema_introspection.get("mutationType")
@@ -363,11 +362,13 @@ def build_directive(directive_introspection: Dict) -> GraphQLDirective:
363362
else []
364363
)
365364

365+
# Then produce and return a Schema with these types.
366366
return GraphQLSchema(
367367
query=query_type,
368368
mutation=mutation_type,
369369
subscription=subscription_type,
370370
types=list(type_map.values()),
371371
directives=directives,
372+
description=schema_introspection.get("description"),
372373
assume_valid=assume_valid,
373374
)

src/graphql/utilities/extend_schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,9 @@ def build_type(ast_node: TypeDefinitionNode) -> GraphQLNamedType:
652652
replace_directive(directive) for directive in schema_kwargs["directives"]
653653
]
654654
+ [build_directive(directive) for directive in directive_defs],
655+
"description": schema_def.description.value
656+
if schema_def and schema_def.description
657+
else None,
655658
"extensions": None,
656659
"ast_node": schema_def or schema_kwargs["ast_node"],
657660
"extension_ast_nodes": (

src/graphql/utilities/get_introspection_query.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@
33
__all__ = ["get_introspection_query"]
44

55

6-
def get_introspection_query(descriptions=True, directive_is_repeatable=False) -> str:
6+
def get_introspection_query(
7+
descriptions=True, directive_is_repeatable=False, schema_description=False
8+
) -> str:
79
"""Get a query for introspection.
810
9-
Optionally, you can exclude descriptions and include repeatability of directives.
11+
Optionally, you can exclude descriptions, include repeatability of directives,
12+
and specify whether to include the schema description as well.
1013
"""
1114
maybe_description = "description" if descriptions else ""
1215
maybe_directive_is_repeatable = "isRepeatable" if directive_is_repeatable else ""
16+
maybe_schema_description = maybe_description if schema_description else ""
1317
return dedent(
1418
f"""
1519
query IntrospectionQuery {{
1620
__schema {{
21+
{maybe_schema_description}
1722
queryType {{ name }}
1823
mutationType {{ name }}
1924
subscriptionType {{ name }}

src/graphql/utilities/introspection_from_schema.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ def introspection_from_schema(
1515
schema: GraphQLSchema,
1616
descriptions: bool = True,
1717
directive_is_repeatable: bool = True,
18+
schema_description: bool = True,
1819
) -> IntrospectionSchema:
1920
"""Build an IntrospectionQuery from a GraphQLSchema
2021
@@ -24,7 +25,11 @@ def introspection_from_schema(
2425
This is the inverse of build_client_schema. The primary use case is outside of the
2526
server context, for instance when doing schema comparisons.
2627
"""
27-
document = parse(get_introspection_query(descriptions, directive_is_repeatable))
28+
document = parse(
29+
get_introspection_query(
30+
descriptions, directive_is_repeatable, schema_description
31+
)
32+
)
2833

2934
from ..execution.execute import execute, ExecutionResult
3035

0 commit comments

Comments
 (0)