pypi graphql-core 3.3.2
v3.3.2

latest release: 3.2.14
one hour ago

Patch-release GraphQL-core v3.3.2, based on GraphQL.js v17.1.0.

This patch-release supports Python 3.10 to 3.15.

This release contains security fixes. It fixes four denial-of-service vulnerabilities in the validator and the parser, so upgrading is recommended for all users of GraphQL-core 3.3. It also includes behavior changes, new features and bug fixes, see below. The security fixes are also available for GraphQL-core 3.2 in version 3.2.14.

Security fixes:

  • MaxIntrospectionDepthRule walked fragments repeatedly, so validating a small document with the default rules could take exponential time. Fragments already checked at the same or a greater depth are now skipped. The rule also no longer raises RecursionError for long fragment chains under an introspection field.
  • Fragments shared by many operations were walked again for every operation, so validation with the default rules took quadratic time. Validation now also stops with "Validation aborted." once the work on shared fragments exceeds 50,000 units (module attribute graphql.validation.validation_context.MAX_FRAGMENT_WORK).
  • The budget of OverlappingFieldsCanBeMergedRule from CVE-2026-75507 counted only field comparisons, so some documents could still take quadratic time. It now covers all of the rule's work (see below).
  • max_tokens from CVE-2026-75508 was only checked after a whole run of comments had been read. It is now enforced while the comments are skipped.

Behavior changes:

  • Documents, fragment chains and variable values nested deeper than 200 levels are now rejected with a GraphQL error instead of raising RecursionError in the request path (#216). GraphQL.js has no such limit. Parsing gives "Syntax Error: Document is nested too deeply. Parsing aborted.", validation gives "Validation aborted.", variable values give "Variable values are nested too deeply." and execution without validation gives "Selections are nested too deeply. Execution aborted." Methods of Parser called directly and utilities such as separate_operations can still raise RecursionError, and inside of fields it stays a field error.
  • The limit also helps on PyPy, where exhausting the stack can crash the interpreter. This is a PyPy issue that newer PyPy versions do not fix yet. Such documents could also use excessive memory there when building error messages. We recommend that users on PyPy upgrade GraphQL-core. Deep recursion elsewhere, for instance in your own code, can still run into the PyPy issue.
  • When the budget of OverlappingFieldsCanBeMergedRule is exhausted, the whole validation now stops with the errors found so far and "Validation aborted.", instead of only this rule stopping. The limit is now graphql.validation.rules.overlapping_fields_can_be_merged.MAX_COMPLEXITY_BUDGET, 500,000 units by default, and also counts comparisons of return type wrappers. MAX_FIELD_COMPARISONS is deprecated. If it is changed, the budget is two times its value and a DeprecationWarning is issued.
  • Conflict messages of this rule are now bounded. Descriptions are abbreviated after 1000 characters, at most 20 locations are kept per side, and fragment spreads with large arguments are described as Name(...).
  • Following a change of the GraphQL specification, empty selection sets are now allowed by default, with the option allow_empty_selection_sets of parse and validate deprecated from the start. A subscription that selects no field is now rejected.
  • Only CRLF, LF and CR now count as line breaks in source locations and block strings, as in GraphQL.js (#294). This changes the locations of errors at a line start, which reported the previous line.
  • NoUndefinedVariablesRule and NoUnusedVariablesRule now report their errors when entering an operation or fragment, which can change the order of the errors.
  • With the experimental @stream, any @stream on one of two fields with the same response name is now a conflict, even with identical directives.

New features:

  • New parser options max_lexical_tokens, max_comments and max_source_length for all parse functions, build_schema, graphql and graphql_sync. max_lexical_tokens works as in GraphQL.js and does not count comments, so combine it with one of the others. max_source_length is measured in code points, where GraphQL.js uses UTF-16 code units.
  • max_tokens is deprecated in favor of these options, without a warning. Unlike in GraphQL.js it is not an alias: it keeps counting comments, so existing code keeps its protection. build_schema now accepts it too.
  • New option max_indent_depth of print_ast. From that depth on, nothing is indented and no newlines are inserted, while block strings keep theirs. Error messages now use at most 100 levels of indentation.

Bug fixes:

  • With @defer, a second labelled defer of the same fragment is no longer dropped, and a non-deferred spread is no longer skipped after a deferred parent.
  • With @defer and @stream, data shared between deferred fragments is no longer lost when another fragment owning it fails, or delayed until a slower fragment finishes. A nested deferred fragment failing through shared work is now announced in pending first and completes only once.
  • Objects implementing __index__ can be serialized as Int and Float again. This was a regression in 3.3.
  • A nullable variable used inside a fragment on a non-null argument with a default is accepted again (#290). This was a regression in 3.3.
  • execute_sync without check_sync now iterates objects that are both iterable and async iterable, such as a Django QuerySet, synchronously instead of returning a coroutine in data (#291).
  • str(error) no longer raises IndexError for sources containing characters like VT, FF, NEL or U+2028, and print_ast formats block strings with these characters or trailing newlines like GraphQL.js (#295).
  • Merging of fragments with arguments is stricter with experimental_fragment_arguments.
  • Conflicts between equal selection sets of fragments on different types were missed in documents parsed with no_location=True.
  • print_location and print_source_location no longer misplace the caret in minified documents at chunk boundaries and at the end of the document.

Performance:

  • Printing is 27 to 33 percent faster on CPython on normal documents and 20 to 29 percent on PyPy on small ones. On deeply nested values the time no longer grows cubically.
  • A cheaper visit() makes validation 10 to 16 percent faster on CPython (#293).
  • SDL validation, execution of fields without arguments and recreating schemas (about 23 percent on CPython) are faster as well (#297, #299, #301).
  • Fields repeated in a selection set are validated much faster, 34 ms instead of 305 ms for 1000 of them on CPython, and errors with many locations no longer scan the source for each of them.

Thanks to Nikhil Agrawal, Mettle Labs (@nikhilagrawalsf) and zx (@manus-use), who independently reported the problem with the introspection depth, to zx also for the reports on shared fragments and the budget, to @eliteaja11-dev for a variant of the latter, and to Zachariah Mithani (AploSec, @zachariah-mithani) for the report on comments. Thanks also to @devkral for reporting the issue with deep nesting, to @rcybulski1122012 and @youdie006 for bug fixes, to @arxhr007 for reporting and fixing the issue with line breaks, and to @dexhunter for the performance improvements.

Thanks to @jkimbo for sponsoring this project.

Don't miss a new graphql-core release

NewReleases is sending notifications on new releases.