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 ) diff --git a/tests/execution/test_variables.py b/tests/execution/test_variables.py index e019f62a..c764045c 100644 --- a/tests/execution/test_variables.py +++ b/tests/execution/test_variables.py @@ -1934,6 +1934,12 @@ 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) + # 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) + assert input_is_nested_too_deeply([[], [[], 1, []], []], deep_type) def ignores_values_that_are_no_objects_for_input_object_types(): input_type = GraphQLInputObjectType(