This is an automated email from the ASF dual-hosted git repository.
Cole-Greer pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.7-dev by this push:
new 75429ec525 Fix gremlin-go PartitionStrategy panic and gremlin-python
ProductiveByStrategy (#3531)
75429ec525 is described below
commit 75429ec525d1d6cc09118e839bd06487709bc1c9
Author: Guian Gumpac <[email protected]>
AuthorDate: Wed Jul 22 16:26:48 2026 -0700
Fix gremlin-go PartitionStrategy panic and gremlin-python
ProductiveByStrategy (#3531)
Assisted-by: Kiro: Claude Opus 4.8
---
CHANGELOG.asciidoc | 2 ++
gremlin-go/driver/strategies.go | 2 +-
gremlin-go/driver/strategies_test.go | 31 ++++++++++++++++++++++
.../python/gremlin_python/process/strategies.py | 2 ++
.../python/tests/unit/process/test_strategies.py | 12 +++++++++
5 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 67c31cc865..e7bb8dd607 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -35,6 +35,8 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Expanded `gremlin-python` CI matrix to test against Python 3.9, 3.10, 3.11,
3.12, and 3.13.
* Add Node 26 support for `gremlin-javascript` and `gremlint`.
* Corrected numerous inaccuracies in the reference documentation, including
wrong default values (connection pool sizes, buffer sizes, ports, timeouts),
stale serializer class names, removed options documented as available, and
broken code examples across the JVM, Python, `.NET`, Go, and JavaScript drivers.
+* Fixed a panic in `gremlin-go` `PartitionStrategy` when `ReadPartitions` was
left unset.
+* Fixed `gremlin-python` `ProductiveByStrategy` to pass through the
`productiveKeys` argument, which was previously accepted but never serialized
to the server.
[[release-3-7-6]]
=== TinkerPop 3.7.6 (Release Date: April 1, 2026)
diff --git a/gremlin-go/driver/strategies.go b/gremlin-go/driver/strategies.go
index b839a07276..5998747d62 100644
--- a/gremlin-go/driver/strategies.go
+++ b/gremlin-go/driver/strategies.go
@@ -86,7 +86,7 @@ func PartitionStrategy(config PartitionStrategyConfig)
TraversalStrategy {
if config.WritePartition != "" {
configMap["writePartition"] = config.WritePartition
}
- if len(config.ReadPartitions.ToSlice()) != 0 {
+ if config.ReadPartitions != nil && len(config.ReadPartitions.ToSlice())
!= 0 {
configMap["readPartitions"] = config.ReadPartitions
}
return &traversalStrategy{name: decorationNamespace +
"PartitionStrategy", configuration: configMap}
diff --git a/gremlin-go/driver/strategies_test.go
b/gremlin-go/driver/strategies_test.go
index 36095586f2..395b50fd71 100644
--- a/gremlin-go/driver/strategies_test.go
+++ b/gremlin-go/driver/strategies_test.go
@@ -414,3 +414,34 @@ func TestStrategy(t *testing.T) {
assert.Equal(t, int32(6), val)
})
}
+
+func Test_StrategyConfig_partitionNilReadPartitions(t *testing.T) {
+ t.Run("Test PartitionStrategy with nil ReadPartitions does not panic",
func(t *testing.T) {
+ config := PartitionStrategyConfig{
+ PartitionKey: "partition",
+ WritePartition: "write",
+ // ReadPartitions intentionally left unset (nil
interface).
+ }
+ var strategy TraversalStrategy
+ assert.NotPanics(t, func() {
+ strategy = PartitionStrategy(config)
+ })
+ assert.NotNil(t, strategy)
+ configMap := strategy.(*traversalStrategy).configuration
+ _, ok := configMap["readPartitions"]
+ assert.False(t, ok)
+ })
+
+ t.Run("Test PartitionStrategy with non-empty ReadPartitions sets
readPartitions", func(t *testing.T) {
+ config := PartitionStrategyConfig{
+ PartitionKey: "partition",
+ WritePartition: "write",
+ ReadPartitions: NewSimpleSet("read"),
+ }
+ strategy := PartitionStrategy(config)
+ assert.NotNil(t, strategy)
+ configMap := strategy.(*traversalStrategy).configuration
+ _, ok := configMap["readPartitions"]
+ assert.True(t, ok)
+ })
+}
diff --git
a/gremlin-python/src/main/python/gremlin_python/process/strategies.py
b/gremlin-python/src/main/python/gremlin_python/process/strategies.py
index f4df2f2df8..10deb6bb26 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/strategies.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/strategies.py
@@ -188,6 +188,8 @@ class PathRetractionStrategy(TraversalStrategy):
class ProductiveByStrategy(TraversalStrategy):
def __init__(self, productiveKeys=None):
TraversalStrategy.__init__(self, fqcn=optimization_namespace +
'ProductiveByStrategy')
+ if productiveKeys is not None:
+ self.configuration["productiveKeys"] = productiveKeys
class CountStrategy(TraversalStrategy):
diff --git
a/gremlin-python/src/main/python/tests/unit/process/test_strategies.py
b/gremlin-python/src/main/python/tests/unit/process/test_strategies.py
index faf5b95a78..1f8846e833 100644
--- a/gremlin-python/src/main/python/tests/unit/process/test_strategies.py
+++ b/gremlin-python/src/main/python/tests/unit/process/test_strategies.py
@@ -110,6 +110,18 @@ class TestTraversalStrategies(object):
assert 1 == len(strategy.configuration)
assert __.has("name","marko") == strategy.configuration["vertices"]
###
+ bytecode =
g.withStrategies(ProductiveByStrategy(productiveKeys=["name", "age"])).bytecode
+ assert 1 == len(bytecode.source_instructions)
+ assert 2 == len(bytecode.source_instructions[0])
+ assert "withStrategies" == bytecode.source_instructions[0][0]
+ assert ProductiveByStrategy() == bytecode.source_instructions[0][1]
+ strategy = bytecode.source_instructions[0][1]
+ assert 1 == len(strategy.configuration)
+ assert ["name", "age"] == strategy.configuration["productiveKeys"]
+ ###
+ bytecode = g.withStrategies(ProductiveByStrategy()).bytecode
+ assert 0 == len(bytecode.source_instructions[0][1].configuration)
+ ###
bytecode = g.withStrategies(OptionsStrategy(options={"x": "test", "y":
True})).bytecode
assert 1 == len(bytecode.source_instructions)
assert 2 == len(bytecode.source_instructions[0])