You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ke...@apache.org on 2024/11/03 05:32:23 UTC
(tinkerpop) 14/48: Custom Strategy Construction (#2806)
This is an automated email from the ASF dual-hosted git repository.
kenhuuu pushed a commit to branch master-http-final
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 4e93c40afab08714ff011feed5fea134a9c9e2b3
Author: Cole Greer <11...@users.noreply.github.com>
AuthorDate: Tue Oct 1 18:49:15 2024 -0700
Custom Strategy Construction (#2806)
This commit adapts TraversalStrategyProxy in Java to work based off of strategy names instead of strategy classes. This change allows the proxy to be used in traversals in order to pass custom/provider defined strategies and configurations into the gremlin script. The advantage of this approach is that the user does not need any concrete strategy class for custom strategies, and the only actions needed from a provider to support a custom strategy is to either register it for use with [...]
---
CHANGELOG.asciidoc | 2 +
docs/src/upgrade/release-4.x.x.asciidoc | 28 +++++++++
.../gremlin/process/traversal/GremlinLang.java | 17 +++++-
.../traversal/strategy/TraversalStrategyProxy.java | 41 ++++++++++++--
.../jsr223/GremlinLangScriptEngineTest.java | 66 ++++++++++++++++++++++
.../strategy/TraversalStrategyProxyTest.java | 64 +++++++++++++++++++++
.../jsr223/GremlinGroovyScriptEngineTest.java | 62 ++++++++++++++++++++
.../python/gremlin_python/process/traversal.py | 3 +-
.../main/python/tests/process/test_strategies.py | 14 +++++
9 files changed, 288 insertions(+), 9 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 3b82297473..55ff8f785e 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -70,6 +70,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Updated `OptionsStrategy` in `gremlin-python` to take options directly as keyword arguments.
* Added static `instance()` method to `ElementIdStrategy` to an instance with the default configuration.
* Updated `ElementIdStrategy.getConfiguration()` to help with serialization.
+* Updated `TraversalStrategyProxy` to utilize strategy names instead of strategy classes
+* Use `TraversalStrategyProxy` in Java, or `TraversalStrategy` in Python to pass custom strategies in traversals
== TinkerPop 3.7.0 (Gremfir Master of the Pan Flute)
diff --git a/docs/src/upgrade/release-4.x.x.asciidoc b/docs/src/upgrade/release-4.x.x.asciidoc
index fcb0713777..2a510001c8 100644
--- a/docs/src/upgrade/release-4.x.x.asciidoc
+++ b/docs/src/upgrade/release-4.x.x.asciidoc
@@ -196,6 +196,34 @@ g.with_strategies(OptionsStrategy(options=myOptions))
g.with_strategies(OptionsStrategy(**myOptions))
----
+==== Custom Traversal Strategy Construction
+
+Traversal strategy construction has been updated such that it is no longer required to have concrete classes for each
+strategy being added to a graph traversal (use of concrete classes remains viable and is recommended for "native"
+TinkerPop strategies). To use strategies without a concrete class, `TraversalStrategyProxy` can be used in Java, and
+`TraversalStrategy` in Python.
+
+All the following examples will produce the script `g.withStrategies(new MyStrategy(config1:'my value',config2:123))`:
+
+[source,java]
+----
+Map<String, Object> configMap = new LinkedHashMap<>();
+ configMap.put("config1", "my value");
+ configMap.put("config2", 123);
+ TraversalStrategy strategyProxy = new TraversalStrategyProxy("MyStrategy", new MapConfiguration(configMap));
+
+ GraphTraversal traversal = g.withStrategies(strategyProxy);
+----
+
+[source,python]
+----
+g.with_strategies(TraversalStrategy(
+ strategy_name='MyStrategy',
+ config1='my value',
+ config2=123
+ ))
+----
+
==== Changes to Serialization
The GLVs will only support GraphBinary V4 and GraphSON support has been removed. This means that the serializer option
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
index 180ea86283..f0ec52cd01 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinLang.java
@@ -22,6 +22,7 @@ package org.apache.tinkerpop.gremlin.process.traversal;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal;
@@ -372,13 +373,23 @@ public class GremlinLang implements Cloneable, Serializable {
break;
}
- final Configuration configuration = ((TraversalStrategy) arguments[i]).getConfiguration();
+ Configuration configuration;
+ String strategyName;
+
+ // special handling for TraversalStrategyProxy
+ if (arguments[i] instanceof TraversalStrategyProxy) {
+ configuration = ((TraversalStrategy) arguments[i]).getConfiguration();
+ strategyName = ((TraversalStrategyProxy) arguments[i]).getStrategyName();
+ } else {
+ configuration = ((TraversalStrategy) arguments[i]).getConfiguration();
+ strategyName = arguments[i].getClass().getSimpleName();
+ }
if (configuration.isEmpty()) {
- sb.append(arguments[i].getClass().getSimpleName());
+ sb.append(strategyName);
} else {
sb.append("new ")
- .append(arguments[i].getClass().getSimpleName())
+ .append(strategyName)
.append("(");
configuration.getKeys().forEachRemaining(key -> {
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxy.java
index 363fd89b84..e1ac0442d6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxy.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxy.java
@@ -20,13 +20,17 @@
package org.apache.tinkerpop.gremlin.process.traversal.strategy;
import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.tinkerpop.gremlin.language.grammar.TraversalStrategyVisitor;
import org.apache.tinkerpop.gremlin.process.traversal.GremlinLang;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import java.io.Serializable;
+import java.util.Collections;
/**
* This class is for use with {@link GremlinLang} and for serialization purposes. It is not meant for direct use with
@@ -37,23 +41,50 @@ import java.io.Serializable;
public final class TraversalStrategyProxy<T extends TraversalStrategy> implements Serializable, TraversalStrategy {
private final Configuration configuration;
- private final Class<T> strategyClass;
+ private final String strategyName;
+
+ public TraversalStrategyProxy(final String strategyName) {
+ this(strategyName, new MapConfiguration(Collections.EMPTY_MAP));
+ }
+
+ public TraversalStrategyProxy(final String strategyName, final Configuration configuration) {
+ this.configuration = configuration;
+ this.strategyName = strategyName;
+ }
public TraversalStrategyProxy(final T traversalStrategy) {
- this((Class<T>) traversalStrategy.getClass(), traversalStrategy.getConfiguration());
+ this(traversalStrategy.getClass().getSimpleName(), traversalStrategy.getConfiguration());
}
+ /**
+ * @deprecated
+ * This constructor has been deprecated since 4.0.0 as TraversalStrategyProxy is now based around strategy names,
+ * instead of strategy classes. Use {@link TraversalStrategyProxy#TraversalStrategyProxy(String, Configuration)}
+ * instead.
+ */
+ @Deprecated
public TraversalStrategyProxy(final Class<T> strategyClass, final Configuration configuration) {
- this.configuration = configuration;
- this.strategyClass = strategyClass;
+ this(strategyClass.getSimpleName(), configuration);
}
public Configuration getConfiguration() {
return this.configuration;
}
+ public String getStrategyName() {
+ return this.strategyName;
+ }
+
+ /**
+ * @deprecated
+ * As of 4.0.0, TraversalStrategyProxy is now based around strategy names, instead of strategy classes. For
+ * compatibility, this method will attempt to lookup the strategy name in {@link TraversalStrategies.GlobalCache}.
+ * Use of {@link TraversalStrategyProxy#getStrategyName()} is preferred. If a class object is needed, users should
+ * utilize a mapping of strategy name to strategy class which is appropriate for their environment.
+ */
+ @Deprecated
public Class<T> getStrategyClass() {
- return this.strategyClass;
+ return (Class<T>) TraversalStrategies.GlobalCache.getRegisteredStrategyClass(strategyName).get();
}
@Override
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
index 9846363030..22dd49659e 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
@@ -18,8 +18,16 @@
*/
package org.apache.tinkerpop.gremlin.jsr223;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.MapConfiguration;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxyTest;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.junit.Test;
@@ -27,9 +35,18 @@ import javax.script.Bindings;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
public class GremlinLangScriptEngineTest {
@@ -59,4 +76,53 @@ public class GremlinLangScriptEngineTest {
assertThat(result, instanceOf(Traversal.Admin.class));
assertEquals(g.V(100, 1000, 10000).asAdmin().getGremlinLang(), ((Traversal.Admin) result).getGremlinLang());
}
+
+ public static class TestStrategy<S extends TraversalStrategy> extends AbstractTraversalStrategy<S> {
+ private final Configuration configuration;
+
+ private TestStrategy(final Configuration configuration) {
+ this.configuration = configuration;
+ }
+ @Override
+ public void apply(Traversal.Admin traversal) {
+ // Do nothing
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public static TestStrategy create(Configuration configuration) {
+ return new TestStrategy(configuration);
+ }
+ }
+
+ @Test
+ public void shouldReconstructCustomRegisteredStrategy() throws ScriptException {
+ TraversalStrategies.GlobalCache.registerStrategy(TestStrategy.class);
+
+ GraphTraversal traversal = (GraphTraversal) scriptEngine.eval("g.withStrategies(new TestStrategy(stringKey:\"stringValue\",intKey:1,booleanKey:true)).V()");
+
+ TestStrategy reconstructedStrategy = traversal.asAdmin().getStrategies().getStrategy(TestStrategy.class).get();
+
+ assertNotNull(reconstructedStrategy);
+
+ MapConfiguration expectedConfig = new MapConfiguration(new HashMap<String, Object>() {{
+ put("stringKey", "stringValue");
+ put("intKey", 1);
+ put("booleanKey", true);
+ }});
+
+ Set<String> expectedKeys = new HashSet<>();
+ Set<String> actualKeys = new HashSet<>();
+ expectedConfig.getKeys().forEachRemaining((key) -> expectedKeys.add(key));
+ reconstructedStrategy.getConfiguration().getKeys().forEachRemaining((key) -> actualKeys.add(key));
+
+ assertEquals(expectedKeys, actualKeys);
+
+ expectedKeys.forEach((key) -> {
+ assertEquals(expectedConfig.get(Object.class, key), reconstructedStrategy.getConfiguration().get(Object.class, key));
+ });
+ }
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxyTest.java
new file mode 100644
index 0000000000..dceb7463b4
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/TraversalStrategyProxyTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal.strategy;
+
+import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class TraversalStrategyProxyTest {
+
+ private static class TestStrategy<S extends TraversalStrategy> extends AbstractTraversalStrategy<S> {
+ @Override
+ public void apply(Traversal.Admin traversal) {
+ // Do nothing
+ }
+ }
+
+ @Test
+ public void shouldAddCustomStrategyToGremlinScript() {
+ TraversalStrategyProxy strategyProxy = new TraversalStrategyProxy("TestStrategy");
+ GraphTraversalSource g = EmptyGraph.instance().traversal();
+ GraphTraversal traversal = g.withStrategies(strategyProxy).V();
+ assertEquals("g.withStrategies(TestStrategy).V()", traversal.asAdmin().getGremlinLang().getGremlin());
+ }
+
+ @Test
+ public void shouldAddCustomStrategyWithConfigToGremlinScript() {
+ Map<String, Object> configMap = new LinkedHashMap<>();
+ configMap.put("stringKey", "stringValue");
+ configMap.put("intKey", 1);
+ configMap.put("booleanKey", true);
+ TraversalStrategyProxy strategyProxy = new TraversalStrategyProxy("TestStrategy", new MapConfiguration(configMap));
+
+ GraphTraversalSource g = EmptyGraph.instance().traversal();
+ GraphTraversal traversal = g.withStrategies(strategyProxy).V();
+
+ assertEquals("g.withStrategies(new TestStrategy(stringKey:\"stringValue\",intKey:1,booleanKey:true)).V()", traversal.asAdmin().getGremlinLang().getGremlin());
+ }
+}
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
index 318ee3dad3..9c8f9acfb4 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java
@@ -21,15 +21,19 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223;
import groovy.lang.Closure;
import groovy.lang.MissingMethodException;
import groovy.lang.MissingPropertyException;
+import org.apache.commons.configuration2.Configuration;
+import org.apache.commons.configuration2.MapConfiguration;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.tinkerpop.gremlin.groovy.jsr223.ast.AmbiguousMethodASTTransformation;
import org.apache.tinkerpop.gremlin.groovy.jsr223.ast.RepeatASTTransformationCustomizer;
import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
import org.apache.tinkerpop.gremlin.structure.Column;
@@ -47,9 +51,11 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -520,4 +526,60 @@ public class GremlinGroovyScriptEngineTest {
final Object values = engine.eval("values");
assertEquals(Column.values, values);
}
+
+ public static class TestStrategy<S extends TraversalStrategy> extends AbstractTraversalStrategy<S> {
+ private final Configuration configuration;
+
+ public TestStrategy(final Map configuration) {
+ this(new MapConfiguration(configuration));
+ }
+
+ private TestStrategy(final Configuration configuration) {
+ this.configuration = configuration;
+ }
+ @Override
+ public void apply(Traversal.Admin traversal) {
+ // Do nothing
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public static TestStrategy create(Configuration configuration) {
+ return new TestStrategy(configuration);
+ }
+ }
+
+ @Test
+ public void shouldReconstructCustomRegisteredStrategy() throws ScriptException {
+ GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(DefaultImportCustomizer.build().addClassImports(TestStrategy.class).create());
+ final GraphTraversalSource g = traversal().withEmbedded(EmptyGraph.instance());
+ final Bindings bindings = new SimpleBindings();
+ bindings.put("g", g);
+
+ GraphTraversal traversal = (GraphTraversal) scriptEngine.eval("g.withStrategies(new TestStrategy(stringKey:\"stringValue\",intKey:1,booleanKey:true)).V()", bindings);
+
+ TestStrategy reconstructedStrategy = traversal.asAdmin().getStrategies().getStrategy(TestStrategy.class).get();
+
+ assertNotNull(reconstructedStrategy);
+
+ MapConfiguration expectedConfig = new MapConfiguration(new HashMap<String, Object>() {{
+ put("stringKey", "stringValue");
+ put("intKey", 1);
+ put("booleanKey", true);
+ }});
+
+ Set<String> expectedKeys = new HashSet<>();
+ Set<String> actualKeys = new HashSet<>();
+ expectedConfig.getKeys().forEachRemaining((key) -> expectedKeys.add(key));
+ reconstructedStrategy.getConfiguration().getKeys().forEachRemaining((key) -> actualKeys.add(key));
+
+ assertEquals(expectedKeys, actualKeys);
+
+ expectedKeys.forEach((key) -> {
+ assertEquals(expectedConfig.get(Object.class, key), reconstructedStrategy.getConfiguration().get(Object.class, key));
+ });
+ }
}
\ No newline at end of file
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 453a7ec6c0..17220e6d18 100644
--- a/gremlin-python/src/main/python/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/python/gremlin_python/process/traversal.py
@@ -726,10 +726,11 @@ class TraversalStrategies(object):
class TraversalStrategy(object):
- def __init__(self, strategy_name=None, configuration=None, fqcn=None):
+ def __init__(self, strategy_name=None, configuration=None, fqcn=None, **kwargs):
self.fqcn = fqcn
self.strategy_name = type(self).__name__ if strategy_name is None else strategy_name
self.configuration = {} if configuration is None else configuration
+ self.configuration = {**kwargs, **self.configuration} # merge additional kwargs into strategy configuration
def apply(self, traversal):
return
diff --git a/gremlin-python/src/main/python/tests/process/test_strategies.py b/gremlin-python/src/main/python/tests/process/test_strategies.py
index 81f428dd14..e03b4f6421 100644
--- a/gremlin-python/src/main/python/tests/process/test_strategies.py
+++ b/gremlin-python/src/main/python/tests/process/test_strategies.py
@@ -93,3 +93,17 @@ class TestTraversalStrategies(object):
options = gremlin_script.options_strategies
assert "test" == options[0].configuration["x"]
assert options[0].configuration["y"]
+
+ def test_custom_strategies(self):
+ g = traversal().with_(None)
+
+ gremlin_script = g.with_strategies(TraversalStrategy(strategy_name='CustomSingletonStrategy')).gremlin_lang
+ assert "withStrategies(CustomSingletonStrategy)" in str(gremlin_script)
+
+ gremlin_script = g.with_strategies(TraversalStrategy(
+ strategy_name='CustomConfigurableStrategy',
+ stringKey='string value',
+ intKey=5,
+ booleanKey=True
+ )).gremlin_lang
+ assert "withStrategies(new CustomConfigurableStrategy(stringKey:'string value',intKey:5,booleanKey:true))" in str(gremlin_script)