This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch GValueFollowupTP4 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 447b77a922230e4617c1b4bc34d5f4d2710bbc61 Author: Cole Greer <[email protected]> AuthorDate: Thu Jun 4 17:43:18 2026 -0700 Add nested-GValue guard across all GLVs Match the Java reference GValue, which forbids wrapping a GValue inside another GValue. Adds a fail-fast guard to the Python, .NET, and Go GValue constructors (JavaScript already includes it in its new implementation), each rejecting a nested GValue with the message "GValues cannot be nested", plus a unit test per GLV. --- gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GValue.cs | 3 +++ .../test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs | 6 ++++++ gremlin-go/driver/gValue.go | 3 +++ gremlin-go/driver/gValue_test.go | 5 +++++ gremlin-python/src/main/python/gremlin_python/process/traversal.py | 2 ++ .../src/main/python/tests/unit/process/test_gremlin_lang.py | 6 ++++++ 6 files changed, 25 insertions(+) diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GValue.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GValue.cs index 97039fb229..71505fe94b 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GValue.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GValue.cs @@ -58,6 +58,9 @@ namespace Gremlin.Net.Process.Traversal /// <exception cref="ArgumentException">Thrown when <paramref name="name" /> is not a valid identifier.</exception> public GValue(string name, T value) { + if (value is IGValue) + throw new ArgumentException("GValues cannot be nested"); + if (name == null) throw new ArgumentNullException(nameof(name), "The parameter name cannot be null."); diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs index f7d4c5ee61..00f27abffc 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/GremlinLangTests.cs @@ -976,6 +976,12 @@ namespace Gremlin.Net.UnitTest.Process.Traversal Assert.Throws<ArgumentException>(() => new GValue<int>("_1", 1)); } + [Fact] + public void GValue_nested_throws_ArgumentException() + { + Assert.Throws<ArgumentException>(() => new GValue<object>("x", new GValue<int>("y", 1))); + } + [Fact] public void GValue_duplicate_name_different_value_throws_ArgumentException() { diff --git a/gremlin-go/driver/gValue.go b/gremlin-go/driver/gValue.go index 32b260da98..79d3fa5d16 100644 --- a/gremlin-go/driver/gValue.go +++ b/gremlin-go/driver/gValue.go @@ -34,6 +34,9 @@ type GValue struct { // NewGValue creates a new GValue to be used in traversals. The name must be non-empty, start with a // Unicode letter, and contain only Unicode letters, digits, or '_'. It cannot begin with "_". func NewGValue(name string, value interface{}) GValue { + if _, ok := value.(GValue); ok { + panic("GValues cannot be nested") + } runes := []rune(name) if len(runes) > 0 && runes[0] == '_' { panic(fmt.Sprintf("invalid GValue name '%v'. Should not start with _.", name)) diff --git a/gremlin-go/driver/gValue_test.go b/gremlin-go/driver/gValue_test.go index 4f93f981b2..5c0fddfa75 100644 --- a/gremlin-go/driver/gValue_test.go +++ b/gremlin-go/driver/gValue_test.go @@ -112,6 +112,11 @@ func TestGValue(t *testing.T) { assert.NotPanics(t, func() { NewGValue("for", 1) }) }) + t.Run("test nested GValue rejected", func(t *testing.T) { + assert.Panics(t, func() { NewGValue("x", NewGValue("y", 1)) }, + "GValues cannot be nested") + }) + t.Run("test distinct but equal slices allowed under same name", func(t *testing.T) { g := NewGraphTraversalSource(nil, nil) param1 := NewGValue("ids", []int{1, 2, 3}) diff --git a/gremlin-python/src/main/python/gremlin_python/process/traversal.py b/gremlin-python/src/main/python/gremlin_python/process/traversal.py index e575f1e572..7264307d4e 100644 --- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py +++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py @@ -1158,6 +1158,8 @@ class GremlinLang(object): class GValue: def __init__(self, name, value): + if isinstance(value, GValue): + raise Exception('GValues cannot be nested') if not name or not name[0].isalpha() or not all(c.isalnum() or c == '_' for c in name[1:]): raise Exception(f'invalid GValue name {name}.') self.name = name diff --git a/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py b/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py index b3e7f4f09c..473f0ad80f 100644 --- a/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py +++ b/gremlin-python/src/main/python/tests/unit/process/test_gremlin_lang.py @@ -570,6 +570,12 @@ class TestGremlinLang(object): assert repr(p) == 'x=1' assert str(p) == 'x=1' + def test_gvalue_cannot_be_nested(self): + try: + GValue('x', GValue('y', 1)) + except Exception as ex: + assert str(ex) == 'GValues cannot be nested' + def test_unsupported_type_throws(self): g = traversal().with_(None) import pytest
