From 431b46e42756695f483d479f432114dafc302824 Mon Sep 17 00:00:00 2001 From: Dixing Xu Date: Tue, 10 Nov 2026 20:13:47 +0000 Subject: [PATCH 1/3] perf(values): depth check keeps one frame per level -- 80% less memory input_is_nested_too_deeply pushed one tuple per list item onto its stack before it popped any, so its memory grew with the width of the list. Keep one frame per nesting level (values, type, depth, next index) and stop the walk of a level when its item type cannot nest. Coercing a [Int] variable of 50,000 ints has a tracemalloc peak of 818,981 bytes, down from 4,091,392 bytes. The returned bool is unchanged. Co-Authored-By: Aiden --- src/graphql/execution/values.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/graphql/execution/values.py b/src/graphql/execution/values.py index c913e399..f40c297d 100644 --- a/src/graphql/execution/values.py +++ b/src/graphql/execution/values.py @@ -214,19 +214,27 @@ def input_is_nested_too_deeply(value: Any, type_: GraphQLInputType) -> bool: :meta private: """ - stack = [(value, type_, 0)] + # Each frame is one nesting level: its values, their type, their depth and the + # index of the next value to check, so that memory grows with depth, not width. + stack: list[tuple[Any, GraphQLInputType, int, int]] = [((value,), type_, 0, 0)] while stack: - value, type_, depth = stack.pop() + values, type_, depth, index = stack.pop() if depth > MAX_NESTING_DEPTH: return True if is_non_null_type(type_): type_ = type_.of_type + if not (is_list_type(type_) or is_input_object_type(type_)): + continue # values of this type cannot nest, so the level is done + if index + 1 < len(values): + stack.append((values, type_, depth, index + 1)) + value = values[index] if is_list_type(type_): - items = value if isinstance(value, (list, tuple)) else [value] - stack.extend((item, type_.of_type, depth + 1) for item in items) + items = value if isinstance(value, (list, tuple)) else (value,) + if items: + stack.append((items, type_.of_type, depth + 1, 0)) elif is_input_object_type(type_) and isinstance(value, dict): stack.extend( - (value[name], field.type, depth + 1) + ((value[name],), field.type, depth + 1, 0) for name, field in type_.fields.items() if name in value ) From 290202bb5127ab68ea3afa4e8b23c3d304115748 Mon Sep 17 00:00:00 2001 From: Dixing Xu Date: Tue, 10 Nov 2026 20:27:38 +0000 Subject: [PATCH 2/3] test(values): cover wide and empty lists in the nesting depth check The depth check keeps one frame per nesting level. No test used a list with several nested items or an empty list, so one line and one branch were not covered and the 100% coverage gate failed on Python 3.14. Co-Authored-By: Aiden --- tests/execution/test_variables.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/execution/test_variables.py b/tests/execution/test_variables.py index e019f62a..79b02769 100644 --- a/tests/execution/test_variables.py +++ b/tests/execution/test_variables.py @@ -1935,6 +1935,19 @@ def checks_list_values_and_single_values_of_list_types(): assert input_is_nested_too_deeply(deep_value, deep_type) assert input_is_nested_too_deeply(1, deep_type) + def checks_all_items_of_wide_and_empty_lists(): + matrix_type = GraphQLList(GraphQLList(GraphQLInt)) + assert not input_is_nested_too_deeply([], matrix_type) + assert not input_is_nested_too_deeply([[], [1, 2], [3]], matrix_type) + deep_type: Any = GraphQLInt + for _ in range(MAX_NESTING_DEPTH + 1): + deep_type = GraphQLList(deep_type) + # an empty list ends the nesting, every other value is nested too deeply + assert not input_is_nested_too_deeply([[], [[], []], []], deep_type) + assert input_is_nested_too_deeply([[], [[], []], 1], deep_type) + assert input_is_nested_too_deeply([1, [[], []], []], deep_type) + assert input_is_nested_too_deeply([[], [[], 1, []], []], deep_type) + def ignores_values_that_are_no_objects_for_input_object_types(): input_type = GraphQLInputObjectType( "NoObject", {"a": GraphQLInputField(GraphQLInt)} From 0259ce748c24dfd82bf19789f058cd6a95967435 Mon Sep 17 00:00:00 2001 From: Dixing Xu Date: Tue, 10 Nov 2026 21:17:37 +0000 Subject: [PATCH 3/3] test(values): fold wide and empty list checks into one depth test Reuse the deep list type of the existing test instead of building it again in a second test. Co-Authored-By: Aiden --- tests/execution/test_variables.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/execution/test_variables.py b/tests/execution/test_variables.py index 79b02769..c764045c 100644 --- a/tests/execution/test_variables.py +++ b/tests/execution/test_variables.py @@ -1934,15 +1934,8 @@ def checks_list_values_and_single_values_of_list_types(): deep_type = GraphQLList(deep_type) assert input_is_nested_too_deeply(deep_value, deep_type) assert input_is_nested_too_deeply(1, deep_type) - - def checks_all_items_of_wide_and_empty_lists(): - matrix_type = GraphQLList(GraphQLList(GraphQLInt)) - assert not input_is_nested_too_deeply([], matrix_type) - assert not input_is_nested_too_deeply([[], [1, 2], [3]], matrix_type) - deep_type: Any = GraphQLInt - for _ in range(MAX_NESTING_DEPTH + 1): - deep_type = GraphQLList(deep_type) # an empty list ends the nesting, every other value is nested too deeply + assert not input_is_nested_too_deeply([], list_type) assert not input_is_nested_too_deeply([[], [[], []], []], deep_type) assert input_is_nested_too_deeply([[], [[], []], 1], deep_type) assert input_is_nested_too_deeply([1, [[], []], []], deep_type)