You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2019/09/23 14:12:07 UTC

[tinkerpop] branch master updated (9b84456 -> 0da112f)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


    from 9b84456  TINKERPOP-2252 Minor refactoring of Translator and more tests
     add 0ac7f3a  TINKERPOP-1810 Support for withSack() that use Lambdas with remotes
     new 49bab07  Merge branch 'TINKERPOP-1810' into tp33
     new 9f500ec  Merge branch 'tp33' into tp34
     new 0da112f  Merge branch 'tp34'

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/reference/gremlin-variants.asciidoc       |  8 +++++
 .../tinkerpop/gremlin/util/function/Lambda.java    | 31 ++++++++++++++++++
 .../src/Gremlin.Net/Process/Traversal/Lambda.cs    |  4 +--
 .../Process/Traversal/StringBasedLambda.cs         | 21 +++++++++++-
 .../GraphTraversalSourceTests.cs                   | 12 +++++++
 .../gremlin/groovy/jsr223/GroovyTranslator.java    | 37 +++++++++++++++++++---
 .../groovy/jsr223/GroovyTranslatorTest.java        | 27 ++++++++++++++++
 .../gremlin_python/structure/io/graphsonV2d0.py    |  8 +++--
 .../gremlin_python/structure/io/graphsonV3d0.py    |  8 +++--
 .../tests/driver/test_driver_remote_connection.py  | 11 +++++++
 11 files changed, 156 insertions(+), 12 deletions(-)


[tinkerpop] 02/03: Merge branch 'tp33' into tp34

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 9f500eca9154d4af89caf09583093dd17a3ba350
Merge: 2e89be3 49bab07
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Mon Sep 23 08:47:16 2019 -0400

    Merge branch 'tp33' into tp34

 CHANGELOG.asciidoc                                 |  1 +
 docs/src/reference/gremlin-variants.asciidoc       |  8 ++++++
 .../tinkerpop/gremlin/util/function/Lambda.java    | 31 ++++++++++++++++++++++
 .../src/Gremlin.Net/Process/Traversal/Lambda.cs    |  4 +--
 .../Process/Traversal/StringBasedLambda.cs         | 21 ++++++++++++++-
 .../GraphTraversalSourceTests.cs                   | 12 +++++++++
 .../gremlin/groovy/jsr223/GroovyTranslator.java    | 31 ++++++++++++++++++++--
 .../groovy/jsr223/GroovyTranslatorTest.java        | 27 +++++++++++++++++++
 .../gremlin_python/structure/io/graphsonV2d0.py    |  8 ++++--
 .../gremlin_python/structure/io/graphsonV3d0.py    |  8 ++++--
 .../tests/driver/test_driver_remote_connection.py  | 11 ++++++++
 11 files changed, 153 insertions(+), 9 deletions(-)

diff --cc docs/src/reference/gremlin-variants.asciidoc
index d54493c,d141333..20b1000
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@@ -779,245 -312,120 +779,249 @@@ g.V().out().map(lambda: "x: len(x.get()
  <7> The default lambda language is changed back to Gremlin-Python.
  <8> If the `lambda`-prefix is not provided, then it is appended automatically in order to give a more natural look to the expression.
  
+ TIP: When running into situations where Groovy cannot properly discern a method signature based on the `Lambda`
+ instance created, it will help to fully define the closure in the lambda expression - so rather than
+ `lambda: ("it.get().value("name')","gremlin-groovy")`, prefer `lambda: ("x -> x.get().value("name"),"gremlin-groovy")`.
+ 
 -=== Limitations
 +Finally, Gremlin `Bytecode` that includes lambdas requires that the traversal be processed by the
 +`ScriptEngine`. To avoid continued recompilation costs, it supports the encoding of bindings, which allow a remote
 +engine to to cache traversals that will be reused over and over again save that some parameterization may change. Thus,
 +instead of translating, compiling, and then executing each submitted bytecode, it is possible to simply execute.
  
 -* Traversals that return a `Set` *might* be coerced to a `List` in Python. In the case of Python, number equality
 -is different from JVM languages which produces different `Set` results when those types are in use. When this case
 -is detected during deserialization, the `Set` is coerced to a `List` so that traversals return consistent
 -results within a collection across different languages. If a `Set` is needed then convert `List` results
 -to `Set` manually.
 +[gremlin-python,modern]
 +----
 +g.V(Bindings.of('id',1)).out('created').map(lambda: ("it.get().value('name').length()", "gremlin-groovy")).sum().toList()
 +g.V(Bindings.of('id',4)).out('created').map(lambda: ("it.get().value('name').length()", "gremlin-groovy")).sum().toList()
 +----
  
 -[[gremlin-DotNet]]
 -== Gremlin.Net
 +==== Native Python Lambdas
  
 -image:gremlin-dotnet-logo.png[width=371,float=right] Apache TinkerPop's Gremlin.Net implements Gremlin within the C# language. It targets .NET Standard and can
 -therefore be used on different operating systems and with different .NET frameworks, such as .NET Framework
 -and link:https://www.microsoft.com/net/core[.NET Core]. Since the C# syntax is very similar to that of Java, it should be very easy to switch between
 -Gremlin-Java and Gremlin.Net. The only major syntactical difference is that all method names in Gremlin.Net
 -use PascalCase as opposed to camelCase in Gremlin-Java in order to comply with .NET conventions.
 +To process lambdas in Python, the `GremlinJythonScriptEngine` must be enabled on the remote end. If that remote is
 +Gremlin Server, then these instructions can help configuration it. As an example, the
 +`conf/gremlin-server-modern-py.yaml` configuration maintains a `GremlinJythonScriptEngine`.
  
 -[source,powershell]
 -nuget install Gremlin.Net
 +[source,bash]
 +----
 +$ bin/gremlin-server.sh install org.apache.tinkerpop gremlin-python x.y.z
 +$ bin/gremlin-server.sh conf/gremlin-server-modern-py.yaml
 +[INFO] GremlinServer -
 +       \,,,/
 +       (o o)
 +---oOOo-(3)-oOOo---
  
 -In Gremlin.Net there exists `GraphTraversalSource`, `GraphTraversal`, and `__` which mirror the respective classes
 -in Gremlin-Java. The `GraphTraversalSource` requires a driver in order to communicate with <<gremlin-server,GremlinServer>> (or any
 -RemoteConnection-enabled server).
 +[INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-modern-py.yaml
 +[INFO] MetricManager - Configured Metrics Slf4jReporter configured with interval=180000ms and loggerName=org.apache.tinkerpop.gremlin.server.Settings$Slf4jReporterMetrics
 +[INFO] GraphManager - Graph [graph] was successfully configured via [conf/tinkergraph-empty.properties].
 +[INFO] ServerGremlinExecutor - Initialized Gremlin thread pool.  Threads in pool named with pattern gremlin-*
 +[INFO] ScriptEngines - Loaded gremlin-jython ScriptEngine
 +[INFO] ScriptEngines - Loaded gremlin-python ScriptEngine
 +[INFO] ScriptEngines - Loaded gremlin-groovy ScriptEngine
 +[INFO] GremlinExecutor - Initialized gremlin-groovy ScriptEngine with scripts/generate-modern.groovy
 +[INFO] ServerGremlinExecutor - Initialized GremlinExecutor and configured ScriptEngines.
 +[INFO] ServerGremlinExecutor - A GraphTraversalSource is now bound to [g] with graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
 +[INFO] OpLoader - Adding the standard OpProcessor.
 +[INFO] OpLoader - Adding the session OpProcessor.
 +[INFO] OpLoader - Adding the traversal OpProcessor.
 +[INFO] TraversalOpProcessor - Initialized cache for TraversalOpProcessor with size 1000 and expiration time of 600000 ms
 +[INFO] GremlinServer - Executing start up LifeCycleHook
 +[INFO] Logger$info - Loading 'modern' graph data.
 +[WARN] AbstractChannelizer - The org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0 serialization class is deprecated.
 +[INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+gryo with org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0
 +[WARN] AbstractChannelizer - The org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0 serialization class is deprecated.
 +[INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+gryo-stringd with org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0
 +[INFO] AbstractChannelizer - Configured application/vnd.gremlin-v3.0+json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0
 +[INFO] AbstractChannelizer - Configured application/json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0
 +[INFO] AbstractChannelizer - Configured application/vnd.graphbinary-v1.0 with org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1
 +[INFO] AbstractChannelizer - Configured application/vnd.graphbinary-v1.0-stringd with org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1
 +[INFO] GremlinServer$1 - Gremlin Server configured with worker thread pool of 1, gremlin pool of 8 and boss thread pool of 1.
 +[INFO] GremlinServer$1 - Channel started at port 8182.
 +----
  
 -The `Gremlin.Net.Driver.Remote.DriverRemoteConnection` is provided as part of Apache TinkerPop’s Gremlin.Net.
 +NOTE: The command to use `install` need only be executed once to gather `gremlin-python` dependencies into Gremlin Servers'
 +path. Future starts of Gremlin Server will not require that command.
  
 -IMPORTANT: For developers wishing to provide another driver implementation, be sure to implement `IRemoteConnection` in
 -`Gremlin.Net.Process.Remote` so it can then be used by Gremlin.Net’s `GraphTraversal`.
 +WARNING: As explained throughout the documentation, when possible <<a-note-on-lambdas,avoid>> lambdas. If lambdas
 +must be used, then consider submitting Groovy lambdas as opposed to Python-based ones. The `GremlinGroovyScriptEngine`
 +is far more featured and performant than its Jython sibling and will likely yield better results.
  
 -When Gremlin Server is running, Gremlin.Net can communicate with Gremlin Server by sending traversals serialized as `Bytecode`.
 +=== Submitting Scripts
  
 -IMPORTANT: Gremlin.Net is not compatible with GraphSON 1.0.
 +WARNING: TinkerPop does not recommend submitting script-based requests and generally continues to support this feature
 +for legacy reasons and corner use cases which are still not completely addressed by the Gremlin language. Please
 +consider using bytecode-based requests instead when possible.
  
 -A traversal source can be spawned anonymously using an `AnonymousTraversalSource` which can be statically imported as
 -follows:
 +The `Client` class implementation/interface is based on the Java Driver, with some restrictions. Most notably,
 +Gremlin-Python does not yet implement the `Cluster` class. Instead, `Client` is instantiated directly.
 +Usage is as follows:
  
 -[source,csharp]
 +[source,python]
  ----
 -using static Gremlin.Net.Process.Traversal.AnonymousTraversalSource;
 +from gremlin_python.driver import client <1>
 +client = client.Client('ws://localhost:8182/gremlin', 'g') <2>
  ----
  
 -which will expose a static `Traversal()` method which can be used as follows to yield a `GraphTraversalSource` assigned
 -to "g" and configured to connect to Gremlin Server at "localhost:8182":
 +<1> Import the Gremlin-Python `client` module.
 +<2> Opens a reference to `localhost` - note that there are various configuration options that can be passed
 +to the `Client` object upon instantiation as keyword arguments.
  
 -[source,csharp]
 +Once a `Client` instance is ready, it is possible to issue some Gremlin:
 +
 +[source,python]
  ----
 -var remoteConnection = new DriverRemoteConnection(new GremlinClient(new GremlinServer("localhost", 8182)));
 -var g = Traversal().WithRemote(remoteConnection);
 +result_set = client.submit("[1,2,3,4]")  <1>
 +future_results = result_set.all()  <2>
 +results = future_results.result() <3>
 +assert results == [1, 2, 3, 4] <4>
 +
 +future_result_set = client.submitAsync("[1,2,3,4]") <5>
 +result_set = future_result_set.result() <6>
 +result = result_set.one() <7>
 +assert results == [1, 2, 3, 4] <8>
 +assert result_set.done.done() <9>
 +
 +client.close() <10>
  ----
  
 -When a traversal from the `GraphTraversalSource` is iterated, the traversal’s `Bytecode` is sent over the wire via the registered
 -`IRemoteConnection`. The bytecode is used to construct the equivalent traversal at the remote traversal source.
 -Moreover, typically the bytecode is analyzed to determine which language the bytecode should be translated to. If the traversal
 -does not contain lambdas, the remote location (e.g. Gremlin Server) will typically
 -use Gremlin-Java. If it has lambdas written in Groovy, it will use Gremlin-Groovy (e.g. `GremlinGroovyScriptEngine`).
 -Likewise, if it has lambdas represented in Python, it will use Gremlin-Python (e.g. `GremlinJythonScriptEngine`).
 +<1> Submit a script that simply returns a `List` of integers.  This method blocks until the request is written to
 +the server and a `ResultSet` is constructed.
 +<2> Even though the `ResultSet` is constructed, it does not mean that the server has sent back the results (or even
 +evaluated the script potentially).  The `ResultSet` is just a holder that is awaiting the results from the server. The `all` method
 +returns a `concurrent.futures.Future` that resolves to a list when it is complete.
 +<3> Block until the the script is evaluated and results are sent back by the server.
 +<4> Verify the result.
 +<5> Submit the same script to the server but don't block.
 +<6> Wait until request is written to the server and `ResultSet` is constructed.
 +<7> Read a single result off the result stream.
 +<8> Again, verify the result.
 +<9> Verify that the all results have been read and stream is closed.
 +<10> Close client and underlying pool connections.
  
 -IMPORTANT: Gremlin.Net’s `ITraversal` interface supports the standard Gremlin methods such as `Next()`, `NextTraverser()`, `ToSet()`,
 -`ToList()`, etc. Such "terminal" methods trigger the evaluation of the traversal.
 +[[gremlin-python-dsl]]
 +=== Domain Specific Languages
  
 -=== RemoteConnection Submission
 +Writing a Gremlin <<dsl,Domain Specific Language>> (DSL) in Python simply requires direct extension of several classes:
  
 -Very similar to Gremlin-Python and Gremlin-Java, there are various ways to submit a traversal to a `IRemoteConnection` using
 -terminal/action methods off of `ITraversal`.
 +* `GraphTraversal` - which exposes the various steps used in traversal writing
 +* `__` - which spawns anonymous traversals from steps
 +* `GraphTraversalSource` - which spawns `GraphTraversal` instances
  
 -* `ITraversal.Next()`
 -* `ITraversal.NextTraverser()`
 -* `ITraversal.ToList()`
 -* `ITraversal.ToSet()`
 -* `ITraversal.Iterate()`
 +The Social DSL based on the link:http://tinkerpop.apache.org/docs/current/images/tinkerpop-modern.png["modern" toy graph]
 +might look like this:
  
 -=== Configuration
 +[source,python]
 +----
 +class SocialTraversal(GraphTraversal):
  
 -The following sections describe how the Gremlin.Net driver can be configured.
 +    def knows(self, person_name):
 +        return self.out("knows").hasLabel("person").has("name", person_name)
  
 -==== Gremlin Server
 +    def youngestFriendsAge(self):
 +        return self.out("knows").hasLabel("person").values("age").min()
  
 -The connection properties for the Gremlin.Net driver can be passed to the `GremlinServer` instance as keyword arguments:
 +    def createdAtLeast(self, number):
 +        return self.outE("created").count().is_(P.gte(number))
  
 -[width="100%",cols="3,10,^2",options="header"]
 -|=========================================================
 -|Key |Description |Default
 -|hostname |The hostname that the driver will connect to. |localhost
 -|port |The port on which Gremlin Server can be reached. |8182
 -|enableSsl |Determines if SSL should be enabled or not. If enabled on the server then it must be enabled on the client. |false
 -|username |The username to submit on requests that require authentication. |_none_
 -|password |The password to submit on requests that require authentication. |_none_
 -|=========================================================
 +class __(AnonymousTraversal):
  
 -==== GraphSON Serialization
 +    graph_traversal = SocialTraversal
  
 -The Gremlin.Net driver uses by default GraphSON 3.0 but it is also possible to use GraphSON 2.0 which can be necessary
 -when the server does not support GraphSON 3.0 yet:
 +    @classmethod
 +    def knows(cls, *args):
 +        return cls.graph_traversal(None, None, Bytecode()).knows(*args)
  
 -[source,csharp]
 +    @classmethod
 +    def youngestFriendsAge(cls, *args):
 +        return cls.graph_traversal(None, None, Bytecode()).youngestFriendsAge(*args)
 +
 +    @classmethod
 +    def createdAtLeast(cls, *args):
 +        return cls.graph_traversal(None, None, Bytecode()).createdAtLeast(*args)
 +
 +
 +class SocialTraversalSource(GraphTraversalSource):
 +
 +    def __init__(self, *args, **kwargs):
 +        super(SocialTraversalSource, self).__init__(*args, **kwargs)
 +        self.graph_traversal = SocialTraversal
 +
 +    def persons(self, *args):
 +        traversal = self.get_graph_traversal()
 +        traversal.bytecode.add_step("V")
 +        traversal.bytecode.add_step("hasLabel", "person")
 +
 +        if len(args) > 0:
 +            traversal.bytecode.add_step("has", "name", P.within(args))
 +
 +        return traversal
 +----
 +
 +NOTE: The `AnonymousTraversal` class above is just an alias for `+__+` as in
 +`+from gremlin_python.process.graph_traversal import __ as AnonymousTraversal+`
 +
 +Using the DSL is straightforward and just requires that the graph instance know the `SocialTraversalSource` should
 +be used:
 +
 +[source,python]
  ----
 -var client = new GremlinClient(new GremlinServer("localhost", 8182), new GraphSON2Reader(),
 -    new GraphSON2Writer(), GremlinClient.GraphSON2MimeType);
 +social = Graph().traversal(SocialTraversalSource).withRemote(DriverRemoteConnection('ws://localhost:8182/gremlin','g'))
 +social.persons("marko").knows("josh")
 +social.persons("marko").youngestFriendsAge()
 +social.persons().filter(__.createdAtLeast(2)).count()
  ----
  
 -=== Static Enums and Methods
 +=== Syntactic Sugar
  
 -Gremlin has various tokens (e.g. `T`, `P`, `Order`, `Operator`, etc.) that are represented in Gremlin.Net as classes.
 +Python supports meta-programming and operator overloading. There are three uses of these techniques in Gremlin-Python
 +that makes traversals a bit more concise.
  
 -These can be used analogously to how they are used in Gremlin-Java.
 +[gremlin-python,modern]
 +----
 +g.V().both()[1:3].toList()
 +g.V().both()[1].toList()
 +g.V().both().name.toList()
 +----
  
 -[source,csharp]
 -g.V().HasLabel("person").Has("age",P.Gt(30)).Order().By("age",Order.desc).ToList()
 +[[gremlin-python-differences]]
 +=== Differences
 +
 +In situations where Python reserved words and global functions overlap with standard Gremlin steps and tokens, those
 +bits of conflicting Gremlin get an underscore appended as a suffix:
  
 -Moreover, the class prefixes can be omitted with a `using static`.
 +*Steps* - <<and-step,and_()>>, <<as-step,as_()>>, <<from-step,from_()>>, <<is-step,is_()>>, <<in-step,in_()>>,
 +<<not-step,not_()>>, <<or-step,or_()>>, <<with-step,with_()>>
 +
 +*Tokens* - <<a-note-on-scopes,Scope.global_>>
 +
 +=== Limitations
 +
 +* Traversals that return a `Set` *might* be coerced to a `List` in Python. In the case of Python, number equality
 +is different from JVM languages which produces different `Set` results when those types are in use. When this case
 +is detected during deserialization, the `Set` is coerced to a `List` so that traversals return consistent
 +results within a collection across different languages. If a `Set` is needed then convert `List` results
 +to `Set` manually.
 +* Gremlin is capable of returning `Dictionary` results that use non-hashable keys (e.g. Dictionary as a key) and Python
 +does not support that at a language level. Gremlin that returns such results will need to be re-written to avoid that.
 +
 +anchor:gremlin-DotNet[]
 +[[gremlin-dotnet]]
 +== Gremlin.Net
 +
 +image:gremlin-dotnet-logo.png[width=371,float=right] Apache TinkerPop's Gremlin.Net implements Gremlin within the C#
 +language. It targets .NET Standard and can therefore be used on different operating systems and with different .NET
 +frameworks, such as .NET Framework and link:https://www.microsoft.com/net/core[.NET Core]. Since the C# syntax is very
 +similar to that of Java, it should be very easy to switch between Gremlin-Java and Gremlin.Net. The only major
 +syntactical difference is that all method names in Gremlin.Net use PascalCase as opposed to camelCase in Gremlin-Java
 +in order to comply with .NET conventions.
 +
 +[source,powershell]
 +nuget install Gremlin.Net
 +
 +=== Connecting
 +
 +The pattern for connecting is described in <<connecting-gremlin,Connecting Gremlin>> and it basically distills down to
 +creating a `GraphTraversalSource`. A `GraphTraversalSource` is created from the `AnonymousTraversalSource.traversal()`
 +method where the "g" provided to the `DriverRemoteConnection` corresponds to the name of a `GraphTraversalSource` on
 +the remote end.
  
  [source,csharp]
  ----
@@@ -1107,82 -508,10 +1111,86 @@@ g.V().Out().Map<int>(Lambda.Python("lam
  The `ILambda` interface returned by these two methods inherits interfaces like `IFunction` and `IPredicate` that mirror
  their Java counterparts which makes it possible to use lambdas with Gremlin.Net for the same steps as in Gremlin-Java.
  
+ TIP: When running into situations where Groovy cannot properly discern a method signature based on the `Lambda`
+ instance created, it will help to fully define the closure in the lambda expression - so rather than
+ `Lambda.Groovy("it.get().value('name'))`, prefer `Lambda.Groovy("x -> x.get().value('name'))`.
+ 
 +=== Submitting Scripts
 +
 +WARNING: TinkerPop does not recommend submitting script-based requests and generally continues to support this feature
 +for legacy reasons and corner use cases which are still not completely addressed by the Gremlin language. Please
 +consider using bytecode-based requests instead when possible.
 +
 +Gremlin scripts are sent to the server from a `IGremlinClient` instance.  A `IGremlinClient` is created as follows:
 +
 +[source,csharp]
 +----
 +include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs[tags=submittingScripts]
 +----
 +
 +If the remote system has authentication and SSL enabled, then the `GremlinServer` object can be configured as follows:
 +
 +[source,csharp]
 +----
 +include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsTests.cs[tags=submittingScriptsWithAuthentication]
 +----
 +
 +[[gremlin-net-dsl]]
 +=== Domain Specific Languages
 +
 +Developing a <<dsl,Domain Specific Language>> (DSL) for .Net is most easily implemented using
 +link:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods[Extension Methods]
 +as they don't require direct extension of classes in the TinkerPop hierarchy. Extension Method classes simply need to
 +be constructed for the `GraphTraversal` and the `GraphTraversalSource`. Unfortunately, anonymous traversals (spawned
 +from `+__+`) can't use the Extension Method approach as they do not work for static classes and static classes can't be
 +extended. The only option is to re-implement the methods of `+__+` as a wrapper in the anonymous traversal for the DSL
 +or to simply create a static class for the DSL and use the two anonymous traversals creators independently. The
 +following example uses the latter approach as it saves a lot of boilerplate code with the minor annoyance of having a
 +second static class to deal with when writing traversals rather than just calling `+__+` for everything.
 +
 +[source,csharp]
 +----
 +include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsDsl.cs[tags=dsl]
 +----
 +
 +Note the creation of `__Social` as the Social DSL's "extension" to the available ways in which to spawn anonymous
 +traversals. The use of the double underscore prefix in the name is just a convention to consider using and is not a
 +requirement. To use the DSL, bring it into scope with the `using` directive:
 +
 +[source,csharp]
 +----
 +include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsDslTests.cs[tags=dslUsing]
 +----
 +
 +and then it can be called from the application as follows:
 +
 +[source,csharp]
 +----
 +include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference/GremlinVariantsDslTests.cs[tags=dslExamples]
 +----
 +
 +anchor:gremlin-dotnet-template[]
 +[[dotnet-application-examples]]
 +=== Application Examples
 +
 +This link:https://docs.microsoft.com/dotnet/core/tools/custom-templates[dotnet template] helps getting started with
 +<<gremlin-dotnet,Gremlin.Net>>. It creates a new C# console project that shows how to connect to a
 +<<gremlin-server,Gremlin Server>> with Gremlin.Net.
 +
 +You can install the template with the dotnet CLI tool:
 +[source,shell]
 +dotnet new -i Gremlin.Net.Template
 +
 +After the template is installed, a new project based on this template can be installed:
 +
 +[source,shell]
 +dotnet new gremlin
 +
 +Specify the output directory for the new project which will then also be used as the name of the created project:
 +
 +[source,shell]
 +dotnet new gremlin -o MyFirstGremlinProject
 +
  [[gremlin-javascript]]
  == Gremlin-JavaScript
  
diff --cc gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index b961821,ba14996..505f88a
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@@ -24,9 -24,9 +24,10 @@@ import org.apache.commons.configuration
  import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
  import org.apache.tinkerpop.gremlin.process.traversal.P;
  import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
 +import org.apache.tinkerpop.gremlin.process.traversal.TextP;
  import org.apache.tinkerpop.gremlin.process.traversal.Translator;
  import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
  import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
  import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
  import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
@@@ -96,158 -98,170 +100,181 @@@ public final class GroovyTranslator imp
          return this.traversalSource;
      }
  
 -    ///////
 +    /**
 +     * Performs standard type translation for the TinkerPop types to Groovy.
 +     */
 +    public static class DefaultTypeTranslator implements TypeTranslator {
  
 -    private String internalTranslate(final String start, final Bytecode bytecode) {
 -        final StringBuilder traversalScript = new StringBuilder(start);
 -        for (final Bytecode.Instruction instruction : bytecode.getInstructions()) {
 -            final String methodName = instruction.getOperator();
 -            if (0 == instruction.getArguments().length)
 -                traversalScript.append(".").append(methodName).append("()");
 -            else {
 -                traversalScript.append(".");
 -                String temp = methodName + "(";
 +        @Override
 +        public Object apply(final String traversalSource, final Object o) {
 +            if (o instanceof Bytecode)
 +                return internalTranslate(traversalSource, (Bytecode) o);
 +            else
 +                return convertToString(o);
 +        }
  
 -                // have to special case withSack() for Groovy because UnaryOperator and BinaryOperator signatures
 -                // make it impossible for the interpreter to figure out which function to call. specifically we need
 -                // to discern between:
 -                //     withSack(A initialValue, UnaryOperator<A> splitOperator)
 -                //     withSack(A initialValue, BinaryOperator<A> splitOperator)
 -                // and:
 -                //     withSack(Supplier<A> initialValue, UnaryOperator<A> mergeOperator)
 -                //     withSack(Supplier<A> initialValue, BinaryOperator<A> mergeOperator)
 -                if (methodName.equals(TraversalSource.Symbols.withSack) &&
 -                        instruction.getArguments().length == 2 && instruction.getArguments()[1] instanceof Lambda) {
 -                    final String castFirstArgTo = instruction.getArguments()[0] instanceof Lambda ?
 -                            Supplier.class.getName() : "";
 -                    final Lambda secondArg = (Lambda) instruction.getArguments()[1];
 -                    final String castSecondArgTo = secondArg.getLambdaArguments() == 1 ? UnaryOperator.class.getName() :
 -                            BinaryOperator.class.getName();
 -                    if (!castFirstArgTo.isEmpty())
 -                        temp = temp + String.format("(%s) ", castFirstArgTo);
 -                    temp = temp + String.format("%s, (%s) %s,",
 -                            convertToString(instruction.getArguments()[0]), castSecondArgTo,
 -                                    convertToString(instruction.getArguments()[1]));
 -                } else {
 -                    for (final Object object : instruction.getArguments()) {
 -                        temp = temp + convertToString(object) + ",";
 -                    }
 +        protected String convertToString(final Object object) {
 +            if (object instanceof Bytecode.Binding)
 +                return ((Bytecode.Binding) object).variable();
 +            else if (object instanceof Bytecode)
 +                return internalTranslate("__", (Bytecode) object);
 +            else if (object instanceof Traversal)
 +                return convertToString(((Traversal) object).asAdmin().getBytecode());
 +            else if (object instanceof String) {
 +                return (((String) object).contains("\"") ? "\"\"\"" + StringEscapeUtils.escapeJava((String) object) + "\"\"\"" : "\"" + StringEscapeUtils.escapeJava((String) object) + "\"")
 +                        .replace("$", "\\$");
 +            } else if (object instanceof Set) {
 +                final Set<String> set = new HashSet<>(((Set) object).size());
 +                for (final Object item : (Set) object) {
 +                    set.add(convertToString(item));
 +                }
 +                return set.toString() + " as Set";
 +            } else if (object instanceof List) {
 +                final List<String> list = new ArrayList<>(((List) object).size());
 +                for (final Object item : (List) object) {
 +                    list.add(convertToString(item));
 +                }
 +                return list.toString();
 +            } else if (object instanceof Map) {
 +                final StringBuilder map = new StringBuilder("[");
 +                for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
 +                    map.append("(").
 +                            append(convertToString(entry.getKey())).
 +                            append("):(").
 +                            append(convertToString(entry.getValue())).
 +                            append("),");
                  }
 -                traversalScript.append(temp.substring(0, temp.length() - 1)).append(")");
 -            }
 -        }
 -        return traversalScript.toString();
 -    }
  
 -    private String convertToString(final Object o) {
 -        // a TypeTranslator that returns Handled means that the typetranslator figure out how to convert the
 -        // object to a string and it should be used as-is, otherwise it gets passed down the line through the normal
 -        // process
 -        final Object object = typeTranslator.apply(o);
 +                // only need to remove this last bit if entries were added
 +                if (!((Map<?, ?>) object).isEmpty())
 +                    map.deleteCharAt(map.length() - 1);
  
 -        if (object instanceof Handled)
 -            return ((Handled) object).getTranslation();
 -        else if (object instanceof Bytecode.Binding)
 -            return ((Bytecode.Binding) object).variable();
 -        else if (object instanceof Bytecode)
 -            return this.internalTranslate("__", (Bytecode) object);
 -        else if (object instanceof Traversal)
 -            return convertToString(((Traversal) object).asAdmin().getBytecode());
 -        else if (object instanceof String) {
 -            return (((String) object).contains("\"") ? "\"\"\"" + StringEscapeUtils.escapeJava((String) object) + "\"\"\"" : "\"" + StringEscapeUtils.escapeJava((String) object) + "\"")
 -                    .replace("$", "\\$");
 -        } else if (object instanceof Set) {
 -            final Set<String> set = new HashSet<>(((Set) object).size());
 -            for (final Object item : (Set) object) {
 -                set.add(convertToString(item));
 -            }
 -            return set.toString() + " as Set";
 -        } else if (object instanceof List) {
 -            final List<String> list = new ArrayList<>(((List) object).size());
 -            for (final Object item : (List) object) {
 -                list.add(convertToString(item));
 -            }
 -            return list.toString();
 -        } else if (object instanceof Map) {
 -            final StringBuilder map = new StringBuilder("[");
 -            for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
 -                map.append("(").
 -                        append(convertToString(entry.getKey())).
 -                        append("):(").
 -                        append(convertToString(entry.getValue())).
 -                        append("),");
 -            }
 +                return map.append("]").toString();
 +            } else if (object instanceof Long)
 +                return object + "L";
 +            else if (object instanceof Double)
 +                return object + "d";
 +            else if (object instanceof Float)
 +                return object + "f";
 +            else if (object instanceof Integer)
 +                return "(int) " + object;
 +            else if (object instanceof Class)
 +                return ((Class) object).getCanonicalName();
 +            else if (object instanceof Timestamp)
 +                return "new java.sql.Timestamp(" + ((Timestamp) object).getTime() + ")";
 +            else if (object instanceof Date)
 +                return "new java.util.Date(" + ((Date) object).getTime() + ")";
 +            else if (object instanceof UUID)
 +                return "java.util.UUID.fromString('" + object.toString() + "')";
 +            else if (object instanceof P)
 +                return convertPToString((P) object, new StringBuilder()).toString();
 +            else if (object instanceof SackFunctions.Barrier)
 +                return "SackFunctions.Barrier." + object.toString();
 +            else if (object instanceof VertexProperty.Cardinality)
 +                return "VertexProperty.Cardinality." + object.toString();
 +            else if (object instanceof TraversalOptionParent.Pick)
 +                return "TraversalOptionParent.Pick." + object.toString();
 +            else if (object instanceof Enum)
 +                return ((Enum) object).getDeclaringClass().getSimpleName() + "." + object.toString();
 +            else if (object instanceof Element) {
 +                if (object instanceof Vertex) {
 +                    final Vertex vertex = (Vertex) object;
 +                    return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
 +                            convertToString(vertex.id()) + "," +
 +                            convertToString(vertex.label()) + ", Collections.emptyMap())";
 +                } else if (object instanceof Edge) {
 +                    final Edge edge = (Edge) object;
 +                    return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(" +
 +                            convertToString(edge.id()) + "," +
 +                            convertToString(edge.label()) + "," +
 +                            "Collections.emptyMap()," +
 +                            convertToString(edge.outVertex().id()) + "," +
 +                            convertToString(edge.outVertex().label()) + "," +
 +                            convertToString(edge.inVertex().id()) + "," +
 +                            convertToString(edge.inVertex().label()) + ")";
 +                } else {// VertexProperty
 +                    final VertexProperty vertexProperty = (VertexProperty) object;
 +                    return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty(" +
 +                            convertToString(vertexProperty.id()) + "," +
 +                            convertToString(vertexProperty.label()) + "," +
 +                            convertToString(vertexProperty.value()) + "," +
 +                            "Collections.emptyMap()," +
 +                            convertToString(vertexProperty.element()) + ")";
 +                }
 +            } else if (object instanceof Lambda) {
 +                final String lambdaString = ((Lambda) object).getLambdaScript().trim();
 +                return lambdaString.startsWith("{") ? lambdaString : "{" + lambdaString + "}";
 +            } else if (object instanceof TraversalStrategyProxy) {
 +                final TraversalStrategyProxy proxy = (TraversalStrategyProxy) object;
 +                if (proxy.getConfiguration().isEmpty())
 +                    return proxy.getStrategyClass().getCanonicalName() + ".instance()";
 +                else
 +                    return proxy.getStrategyClass().getCanonicalName() + ".create(new org.apache.commons.configuration.MapConfiguration(" + convertToString(ConfigurationConverter.getMap(proxy.getConfiguration())) + "))";
 +            } else if (object instanceof TraversalStrategy) {
 +                return convertToString(new TraversalStrategyProxy(((TraversalStrategy) object)));
 +            } else
 +                return null == object ? "null" : object.toString();
 +        }
  
 -            // only need to remove this last bit if entries were added
 -            if (!((Map<?, ?>) object).isEmpty())
 -                map.deleteCharAt(map.length() - 1);
 +        protected String internalTranslate(final String start, final Bytecode bytecode) {
 +            final StringBuilder traversalScript = new StringBuilder(start);
 +            for (final Bytecode.Instruction instruction : bytecode.getInstructions()) {
 +                final String methodName = instruction.getOperator();
 +                if (0 == instruction.getArguments().length)
 +                    traversalScript.append(".").append(methodName).append("()");
 +                else {
 +                    traversalScript.append(".");
 +                    String temp = methodName + "(";
-                     for (final Object object : instruction.getArguments()) {
-                         temp = temp + convertToString(object) + ",";
+ 
 -            return map.append("]").toString();
 -        } else if (object instanceof Long)
 -            return object + "L";
 -        else if (object instanceof Double)
 -            return object + "d";
 -        else if (object instanceof Float)
 -            return object + "f";
 -        else if (object instanceof Integer)
 -            return "(int) " + object;
 -        else if (object instanceof Class)
 -            return ((Class) object).getCanonicalName();
 -        else if (object instanceof Timestamp)
 -            return "new java.sql.Timestamp(" + ((Timestamp) object).getTime() + ")";
 -        else if (object instanceof Date)
 -            return "new java.util.Date(" + ((Date) object).getTime() + ")";
 -        else if (object instanceof UUID)
 -            return "java.util.UUID.fromString('" + object.toString() + "')";
 -        else if (object instanceof P)
 -            return convertPToString((P) object, new StringBuilder()).toString();
 -        else if (object instanceof SackFunctions.Barrier)
 -            return "SackFunctions.Barrier." + object.toString();
 -        else if (object instanceof VertexProperty.Cardinality)
 -            return "VertexProperty.Cardinality." + object.toString();
 -        else if (object instanceof TraversalOptionParent.Pick)
 -            return "TraversalOptionParent.Pick." + object.toString();
 -        else if (object instanceof Enum)
 -            return ((Enum) object).getDeclaringClass().getSimpleName() + "." + object.toString();
 -        else if (object instanceof Element) {
 -            if (object instanceof Vertex) {
 -                final Vertex vertex = (Vertex) object;
 -                return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(" +
 -                        convertToString(vertex.id()) + "," +
 -                        convertToString(vertex.label()) + ", Collections.emptyMap())";
 -            } else if (object instanceof Edge) {
 -                final Edge edge = (Edge) object;
 -                return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(" +
 -                        convertToString(edge.id()) + "," +
 -                        convertToString(edge.label()) + "," +
 -                        "Collections.emptyMap()," +
 -                        convertToString(edge.outVertex().id()) + "," +
 -                        convertToString(edge.outVertex().label()) + "," +
 -                        convertToString(edge.inVertex().id()) + "," +
 -                        convertToString(edge.inVertex().label()) + ")";
 -            } else {// VertexProperty
 -                final VertexProperty vertexProperty = (VertexProperty) object;
 -                return "new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty(" +
 -                        convertToString(vertexProperty.id()) + "," +
 -                        convertToString(vertexProperty.label()) + "," +
 -                        convertToString(vertexProperty.value()) + "," +
 -                        "Collections.emptyMap()," +
 -                        convertToString(vertexProperty.element()) + ")";
++                    // have to special case withSack() for Groovy because UnaryOperator and BinaryOperator signatures
++                    // make it impossible for the interpreter to figure out which function to call. specifically we need
++                    // to discern between:
++                    //     withSack(A initialValue, UnaryOperator<A> splitOperator)
++                    //     withSack(A initialValue, BinaryOperator<A> splitOperator)
++                    // and:
++                    //     withSack(Supplier<A> initialValue, UnaryOperator<A> mergeOperator)
++                    //     withSack(Supplier<A> initialValue, BinaryOperator<A> mergeOperator)
++                    if (methodName.equals(TraversalSource.Symbols.withSack) &&
++                            instruction.getArguments().length == 2 && instruction.getArguments()[1] instanceof Lambda) {
++                        final String castFirstArgTo = instruction.getArguments()[0] instanceof Lambda ?
++                                Supplier.class.getName() : "";
++                        final Lambda secondArg = (Lambda) instruction.getArguments()[1];
++                        final String castSecondArgTo = secondArg.getLambdaArguments() == 1 ? UnaryOperator.class.getName() :
++                                BinaryOperator.class.getName();
++                        if (!castFirstArgTo.isEmpty())
++                            temp = temp + String.format("(%s) ", castFirstArgTo);
++                        temp = temp + String.format("%s, (%s) %s,",
++                                convertToString(instruction.getArguments()[0]), castSecondArgTo,
++                                convertToString(instruction.getArguments()[1]));
++                    } else {
++                        for (final Object object : instruction.getArguments()) {
++                            temp = temp + convertToString(object) + ",";
++                        }
 +                    }
 +                    traversalScript.append(temp.substring(0, temp.length() - 1)).append(")");
 +                }
              }
 -        } else if (object instanceof Lambda) {
 -            final String lambdaString = ((Lambda) object).getLambdaScript().trim();
 -            return lambdaString.startsWith("{") ? lambdaString : "{" + lambdaString + "}";
 -        } else if (object instanceof TraversalStrategyProxy) {
 -            final TraversalStrategyProxy proxy = (TraversalStrategyProxy) object;
 -            if (proxy.getConfiguration().isEmpty())
 -                return proxy.getStrategyClass().getCanonicalName() + ".instance()";
 -            else
 -                return proxy.getStrategyClass().getCanonicalName() + ".create(new org.apache.commons.configuration.MapConfiguration(" + convertToString(ConfigurationConverter.getMap(proxy.getConfiguration())) + "))";
 -        } else if (object instanceof TraversalStrategy) {
 -            return convertToString(new TraversalStrategyProxy(((TraversalStrategy) object)));
 -        } else
 -            return null == object ? "null" : object.toString();
 -    }
 +            return traversalScript.toString();
 +        }
  
 -    private StringBuilder convertPToString(final P p, final StringBuilder current) {
 -        if (p instanceof ConnectiveP) {
 -            final List<P<?>> list = ((ConnectiveP) p).getPredicates();
 -            for (int i = 0; i < list.size(); i++) {
 -                convertPToString(list.get(i), current);
 -                if (i < list.size() - 1)
 -                    current.append(p instanceof OrP ? ".or(" : ".and(");
 -            }
 -            current.append(")");
 -        } else
 -            current.append("P.").append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
 -        return current;
 +        protected StringBuilder convertPToString(final P p, final StringBuilder current) {
 +            if (p instanceof TextP) return convertTextPToString((TextP) p, current);
 +            if (p instanceof ConnectiveP) {
 +                final List<P<?>> list = ((ConnectiveP) p).getPredicates();
 +                for (int i = 0; i < list.size(); i++) {
 +                    convertPToString(list.get(i), current);
 +                    if (i < list.size() - 1)
 +                        current.append(p instanceof OrP ? ".or(" : ".and(");
 +                }
 +                current.append(")");
 +            } else
 +                current.append("P.").append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
 +            return current;
 +        }
 +
 +        protected StringBuilder convertTextPToString(final TextP p, final StringBuilder current) {
 +            current.append("TextP.").append(p.getBiPredicate().toString()).append("(").append(convertToString(p.getValue())).append(")");
 +            return current;
 +        }
      }
  }


[tinkerpop] 01/03: Merge branch 'TINKERPOP-1810' into tp33

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 49bab075b4306cb50f9ae9fb95bb9a3e96ac821a
Merge: 79ce819 0ac7f3a
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Mon Sep 23 06:42:52 2019 -0400

    Merge branch 'TINKERPOP-1810' into tp33

 CHANGELOG.asciidoc                                 |  1 +
 docs/src/reference/gremlin-variants.asciidoc       |  8 ++++++
 .../tinkerpop/gremlin/util/function/Lambda.java    | 31 ++++++++++++++++++++++
 .../src/Gremlin.Net/Process/Traversal/Lambda.cs    |  4 +--
 .../Process/Traversal/StringBasedLambda.cs         | 21 ++++++++++++++-
 .../GraphTraversalSourceTests.cs                   | 12 +++++++++
 .../gremlin/groovy/jsr223/GroovyTranslator.java    | 31 ++++++++++++++++++++--
 .../groovy/jsr223/GroovyTranslatorTest.java        | 28 ++++++++++++++++++-
 .../gremlin_python/structure/io/graphsonV2d0.py    |  8 ++++--
 .../gremlin_python/structure/io/graphsonV3d0.py    |  8 ++++--
 .../tests/driver/test_driver_remote_connection.py  | 11 ++++++++
 11 files changed, 153 insertions(+), 10 deletions(-)

diff --cc CHANGELOG.asciidoc
index bf7ef39,2797d91..1cc3600
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@@ -23,12 -23,10 +23,13 @@@ image::https://raw.githubusercontent.co
  [[release-3-3-9]]
  === TinkerPop 3.3.9 (Release Date: NOT OFFICIALLY RELEASED YET)
  
 +* Exposed response status attributes in a `ResponseError` in gremlin-javascript.
  * Added `ImmutableExplanation` for a `TraversalExplanation` that just contains data.
+ * Added support for `UnaryOperator` and `BinaryOperator` for `Lambda` instances.
  * Fixed `TraversalExplanation` deserialization in GraphSON 2 and 3 which was not supported before in Java.
  * Added support for custom request headers in Python.
 +* Deprecated `scriptEvaluationTimeout` in favor of the more generic `evaluationTimeout`.
 +* Update jackson databind 2.9.9.3.
  * Bumped jackson databind 2.9.9.3.
  * Fixed Java driver authentication problems when calling the driver from multiple threads.
  * Modified Java driver to use IP address rather than hostname to create connections.


[tinkerpop] 03/03: Merge branch 'tp34'

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 0da112f9eb94ee35b39e5d3d73f5507b83b0a5b0
Merge: 9b84456 9f500ec
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Mon Sep 23 10:11:46 2019 -0400

    Merge branch 'tp34'

 CHANGELOG.asciidoc                                 |  1 +
 docs/src/reference/gremlin-variants.asciidoc       |  8 +++++
 .../tinkerpop/gremlin/util/function/Lambda.java    | 31 ++++++++++++++++++
 .../src/Gremlin.Net/Process/Traversal/Lambda.cs    |  4 +--
 .../Process/Traversal/StringBasedLambda.cs         | 21 +++++++++++-
 .../GraphTraversalSourceTests.cs                   | 12 +++++++
 .../gremlin/groovy/jsr223/GroovyTranslator.java    | 37 +++++++++++++++++++---
 .../groovy/jsr223/GroovyTranslatorTest.java        | 27 ++++++++++++++++
 .../gremlin_python/structure/io/graphsonV2d0.py    |  8 +++--
 .../gremlin_python/structure/io/graphsonV3d0.py    |  8 +++--
 .../tests/driver/test_driver_remote_connection.py  | 11 +++++++
 11 files changed, 156 insertions(+), 12 deletions(-)

diff --cc gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
index 9cd06e7,505f88a..1c21d59
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslator.java
@@@ -41,16 -41,17 +42,16 @@@ import org.apache.tinkerpop.gremlin.str
  import org.apache.tinkerpop.gremlin.util.function.Lambda;
  
  import java.sql.Timestamp;
--import java.util.ArrayList;
  import java.util.Date;
--import java.util.HashSet;
 +import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
--import java.util.Optional;
  import java.util.Set;
  import java.util.UUID;
 +import java.util.stream.Collectors;
+ import java.util.function.BinaryOperator;
+ import java.util.function.Supplier;
+ import java.util.function.UnaryOperator;
  
  /**
   * Converts bytecode to a Groovy string of Gremlin.
@@@ -281,46 -203,62 +282,72 @@@ public final class GroovyTranslator imp
                  }
              } else if (object instanceof Lambda) {
                  final String lambdaString = ((Lambda) object).getLambdaScript().trim();
 -                return lambdaString.startsWith("{") ? lambdaString : "{" + lambdaString + "}";
 +                final String wrapper = lambdaString.startsWith("{") ? lambdaString : "{" + lambdaString + "}";
 +                return script.getBoundKeyOrAssign(withParameters, withParameters ? object : wrapper);
              } else if (object instanceof TraversalStrategyProxy) {
                  final TraversalStrategyProxy proxy = (TraversalStrategyProxy) object;
 -                if (proxy.getConfiguration().isEmpty())
 -                    return proxy.getStrategyClass().getCanonicalName() + ".instance()";
 -                else
 -                    return proxy.getStrategyClass().getCanonicalName() + ".create(new org.apache.commons.configuration.MapConfiguration(" + convertToString(ConfigurationConverter.getMap(proxy.getConfiguration())) + "))";
 +                if (proxy.getConfiguration().isEmpty()) {
 +                    return script.append(proxy.getStrategyClass().getCanonicalName() + ".instance()");
 +                } else {
 +                    script.append(proxy.getStrategyClass().getCanonicalName() + ".create(new org.apache.commons.configuration2.MapConfiguration(");
 +                    convertToScript(ConfigurationConverter.getMap(proxy.getConfiguration()));
 +                    return script.append("))");
 +                }
              } else if (object instanceof TraversalStrategy) {
 -                return convertToString(new TraversalStrategyProxy(((TraversalStrategy) object)));
 -            } else
 -                return null == object ? "null" : object.toString();
 +                return convertToScript(new TraversalStrategyProxy(((TraversalStrategy) object)));
 +            } else {
 +                return null == object ? script.append("null") : script.getBoundKeyOrAssign(withParameters, object);
 +            }
          }
  
 -        protected String internalTranslate(final String start, final Bytecode bytecode) {
 -            final StringBuilder traversalScript = new StringBuilder(start);
 +        protected Script internalTranslate(final String start, final Bytecode bytecode) {
 +            script.append(start);
              for (final Bytecode.Instruction instruction : bytecode.getInstructions()) {
                  final String methodName = instruction.getOperator();
 -                if (0 == instruction.getArguments().length)
 -                    traversalScript.append(".").append(methodName).append("()");
 -                else {
 -                    traversalScript.append(".");
 -                    String temp = methodName + "(";
 +                if (0 == instruction.getArguments().length) {
 +                    script.append(".").append(methodName).append("()");
 +                } else {
 +                    script.append(".").append(methodName).append("(");
-                     for (final Object object : instruction.getArguments()) {
-                         convertToScript(object);
+ 
+                     // have to special case withSack() for Groovy because UnaryOperator and BinaryOperator signatures
+                     // make it impossible for the interpreter to figure out which function to call. specifically we need
+                     // to discern between:
+                     //     withSack(A initialValue, UnaryOperator<A> splitOperator)
+                     //     withSack(A initialValue, BinaryOperator<A> splitOperator)
+                     // and:
+                     //     withSack(Supplier<A> initialValue, UnaryOperator<A> mergeOperator)
+                     //     withSack(Supplier<A> initialValue, BinaryOperator<A> mergeOperator)
+                     if (methodName.equals(TraversalSource.Symbols.withSack) &&
+                             instruction.getArguments().length == 2 && instruction.getArguments()[1] instanceof Lambda) {
+                         final String castFirstArgTo = instruction.getArguments()[0] instanceof Lambda ?
+                                 Supplier.class.getName() : "";
+                         final Lambda secondArg = (Lambda) instruction.getArguments()[1];
+                         final String castSecondArgTo = secondArg.getLambdaArguments() == 1 ? UnaryOperator.class.getName() :
+                                 BinaryOperator.class.getName();
+                         if (!castFirstArgTo.isEmpty())
 -                            temp = temp + String.format("(%s) ", castFirstArgTo);
 -                        temp = temp + String.format("%s, (%s) %s,",
 -                                convertToString(instruction.getArguments()[0]), castSecondArgTo,
 -                                convertToString(instruction.getArguments()[1]));
++                            script.append(String.format("(%s) ", castFirstArgTo));
++                        convertToScript(instruction.getArguments()[0]);
++                        script.append(", (");
++                        script.append(castSecondArgTo);
++                        script.append(") ");
++                        convertToScript(instruction.getArguments()[1]);
 +                        script.append(",");
+                     } else {
+                         for (final Object object : instruction.getArguments()) {
 -                            temp = temp + convertToString(object) + ",";
++                            convertToScript(object);
++                            script.append(",");
+                         }
                      }
 -                    traversalScript.append(temp.substring(0, temp.length() - 1)).append(")");
 +                    script.setCharAtEnd(')');
                  }
              }
 -            return traversalScript.toString();
 +            return script;
          }
  
 -        protected StringBuilder convertPToString(final P p, final StringBuilder current) {
 -            if (p instanceof TextP) return convertTextPToString((TextP) p, current);
 +        protected Script convertPToScript(final P p) {
 +            if (p instanceof TextP) {
 +                return convertTextPToScript((TextP) p);
 +            }
              if (p instanceof ConnectiveP) {
                  final List<P<?>> list = ((ConnectiveP) p).getPredicates();
                  for (int i = 0; i < list.size(); i++) {
diff --cc gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
index 420d7fe,71f79ec..523c19f
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyTranslatorTest.java
@@@ -102,6 -102,32 +103,32 @@@ public class GroovyTranslatorTest 
      }
  
      @Test
+     public void shouldHandleConfusingSacks() {
+         final TinkerGraph graph = TinkerFactory.createModern();
+         final GraphTraversalSource g = graph.traversal();
+ 
+         final Traversal<Vertex,Double> tConstantUnary = g.withSack(1.0, Lambda.unaryOperator("it + 1")).V().sack();
 -        final String scriptConstantUnary = GroovyTranslator.of("g").translate(tConstantUnary.asAdmin().getBytecode());
++        final String scriptConstantUnary = GroovyTranslator.of("g").translate(tConstantUnary.asAdmin().getBytecode()).getScript();
+         assertEquals("g.withSack(1.0d, (java.util.function.UnaryOperator) {it + 1}).V().sack()", scriptConstantUnary);
+         assertThatScriptOk(scriptConstantUnary, "g", g);
+ 
+         final Traversal<Vertex,Double> tSupplierUnary = g.withSack(Lambda.supplier("1.0d"), Lambda.<Double>unaryOperator("it + 1")).V().sack();
 -        final String scriptSupplierUnary = GroovyTranslator.of("g").translate(tSupplierUnary.asAdmin().getBytecode());
++        final String scriptSupplierUnary = GroovyTranslator.of("g").translate(tSupplierUnary.asAdmin().getBytecode()).getScript();
+         assertEquals("g.withSack((java.util.function.Supplier) {1.0d}, (java.util.function.UnaryOperator) {it + 1}).V().sack()", scriptSupplierUnary);
+         assertThatScriptOk(scriptSupplierUnary, "g", g);
+ 
+         final Traversal<Vertex,Double> tConstantBinary = g.withSack(1.0, Lambda.binaryOperator("x,y -> x + y + 1")).V().sack();
 -        final String scriptConstantBinary = GroovyTranslator.of("g").translate(tConstantBinary.asAdmin().getBytecode());
++        final String scriptConstantBinary = GroovyTranslator.of("g").translate(tConstantBinary.asAdmin().getBytecode()).getScript();
+         assertEquals("g.withSack(1.0d, (java.util.function.BinaryOperator) {x,y -> x + y + 1}).V().sack()", scriptConstantBinary);
+         assertThatScriptOk(scriptConstantBinary, "g", g);
+ 
+         final Traversal<Vertex,Double> tSupplierBinary = g.withSack(Lambda.supplier("1.0d"), Lambda.<Double>binaryOperator("x,y -> x + y + 1")).V().sack();
 -        final String scriptSupplierBinary = GroovyTranslator.of("g").translate(tSupplierBinary.asAdmin().getBytecode());
++        final String scriptSupplierBinary = GroovyTranslator.of("g").translate(tSupplierBinary.asAdmin().getBytecode()).getScript();
+         assertEquals("g.withSack((java.util.function.Supplier) {1.0d}, (java.util.function.BinaryOperator) {x,y -> x + y + 1}).V().sack()", scriptSupplierBinary);
+         assertThatScriptOk(scriptSupplierBinary, "g", g);
+     }
+ 
+     @Test
      public void shouldSupportStringSupplierLambdas() {
          final TinkerGraph graph = TinkerFactory.createModern();
          GraphTraversalSource g = graph.traversal();