@@ -97,6 +97,7 @@ def parse(
9797 no_location : bool = False ,
9898 max_tokens : int | None = None ,
9999 experimental_fragment_arguments : bool = False ,
100+ allow_empty_selection_sets : bool = True ,
100101) -> DocumentNode :
101102 """Given a GraphQL source, parse it into a Document.
102103
@@ -132,6 +133,17 @@ def parse(
132133 ...B(x: $var)
133134 }
134135
136+ If ``allow_empty_selection_sets`` is set to ``True`` (the default), the parser
137+ accepts selection sets that contain no selections, changing the grammar from
138+ ``SelectionSet : { Selection+ }`` to ``SelectionSet : { Selection* }``.
139+ See https://github.com/graphql/graphql-spec/pull/1227
140+
141+ For example::
142+
143+ {
144+ viewer { }
145+ }
146+
135147 :param source: A GraphQL source string or source object.
136148 :param no_location: By default, the parser creates AST nodes that know the
137149 location in the source that they correspond to. Setting this parameter to
@@ -143,6 +155,9 @@ def parse(
143155 number of tokens allowed within a document.
144156 :param experimental_fragment_arguments: Allows fragment variable definitions
145157 and arguments on fragment spreads to be parsed (experimental).
158+ :param allow_empty_selection_sets: Allows selection sets without selections
159+ (the default). Deprecated, since empty selection sets are now always allowed;
160+ this option will be removed in a future version.
146161 :returns: The parsed GraphQL document AST.
147162
148163 Parse a GraphQL document with the default parser options:
@@ -176,6 +191,7 @@ def parse(
176191 no_location = no_location ,
177192 max_tokens = max_tokens ,
178193 experimental_fragment_arguments = experimental_fragment_arguments ,
194+ allow_empty_selection_sets = allow_empty_selection_sets ,
179195 )
180196 return parser .parse_document ()
181197
@@ -375,6 +391,7 @@ class Parser:
375391 _no_location : bool
376392 _max_tokens : int | None
377393 _experimental_fragment_arguments : bool
394+ _allow_empty_selection_sets : bool
378395 _lexer : Lexer
379396 _token_counter : int
380397
@@ -385,13 +402,15 @@ def __init__(
385402 max_tokens : int | None = None ,
386403 experimental_fragment_arguments : bool = False ,
387404 lexer : Lexer | None = None ,
405+ allow_empty_selection_sets : bool = True ,
388406 ) -> None :
389407 if not is_source (source ):
390408 source = Source (cast ("str" , source ))
391409
392410 self ._no_location = no_location
393411 self ._max_tokens = max_tokens
394412 self ._experimental_fragment_arguments = experimental_fragment_arguments
413+ self ._allow_empty_selection_sets = allow_empty_selection_sets
395414 # You may override the lexer used to lex the source; this is used by schema
396415 # coordinates to introduce a lexer with a restricted syntax.
397416 self ._lexer = lexer if lexer is not None else Lexer (source )
@@ -562,11 +581,23 @@ def parse_variable(self) -> VariableNode:
562581 return VariableNode (name = self .parse_name (), loc = self .loc (start ))
563582
564583 def parse_selection_set (self ) -> SelectionSetNode :
565- """SelectionSet: {Selection+}"""
584+ """SelectionSet: {Selection+}
585+
586+ With ``allow_empty_selection_sets`` enabled (the default):
587+
588+ SelectionSet: {Selection*}
589+
590+ The ``allow_empty_selection_sets`` option is deprecated and will be removed in
591+ a future version, at which point this will be the only grammar.
592+ """
566593 start = self ._lexer .token
567594 return SelectionSetNode (
568- selections = self .many (
569- TokenKind .BRACE_L , self .parse_selection , TokenKind .BRACE_R
595+ selections = (
596+ self .any (TokenKind .BRACE_L , self .parse_selection , TokenKind .BRACE_R )
597+ if self ._allow_empty_selection_sets
598+ else self .many (
599+ TokenKind .BRACE_L , self .parse_selection , TokenKind .BRACE_R
600+ )
570601 ),
571602 loc = self .loc (start ),
572603 )
0 commit comments