You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/08/17 20:17:52 UTC

tinkerpop git commit: RemoteGraph.py no longer exists. Its now just Graph().traversal().withRemote(). Found a severe bug in TraversalStrategies.py that has now been fixed. Updated the gremlin-variants.asciidoc with latest changes and combed through it fu

Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1278 b4c613981 -> 4a6645517


RemoteGraph.py no longer exists. Its now just Graph().traversal().withRemote(). Found a severe bug in TraversalStrategies.py that has now been fixed. Updated the gremlin-variants.asciidoc with latest changes and combed through it fully to clean up the text and examples as best as possible. Updated gremlin-server-modern-py.yaml to include both gremlin-jython and gremlin-python ScriptEngines as there truly is a difference and they need to be kept distinct even if they point to the same ultimate GremlinJythonScriptEngine.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/4a664551
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/4a664551
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/4a664551

Branch: refs/heads/TINKERPOP-1278
Commit: 4a6645517325612faa78b6a6f86417e318fc0b89
Parents: b4c6139
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Wed Aug 17 14:17:47 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Wed Aug 17 14:17:47 2016 -0600

----------------------------------------------------------------------
 docs/preprocessor/awk/init-code-blocks.awk      |  7 +-
 docs/src/reference/gremlin-variants.asciidoc    | 98 +++++++++++---------
 .../process/traversal/TraversalSource.java      |  1 +
 .../python/GraphTraversalSourceGenerator.groovy | 15 ++-
 .../python/TraversalSourceGenerator.groovy      |  7 +-
 .../jython/gremlin_python/driver/__init__.py    |  7 +-
 .../gremlin_python/driver/remote_connection.py  | 13 +++
 .../gremlin_python/process/graph_traversal.py   | 24 ++---
 .../jython/gremlin_python/process/traversal.py  |  7 +-
 .../jython/gremlin_python/structure/__init__.py |  1 -
 .../jython/gremlin_python/structure/graph.py    |  6 ++
 .../gremlin_python/structure/remote_graph.py    | 45 ---------
 .../driver/WebSocketRemoteConnectionTest.java   |  4 +-
 .../python/jsr223/JythonScriptEngineSetup.java  |  2 +-
 .../jsr223/PythonGraphSONJavaTranslator.java    |  2 +-
 .../conf/gremlin-server-modern-py.yaml          |  4 +-
 16 files changed, 124 insertions(+), 119 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/docs/preprocessor/awk/init-code-blocks.awk
----------------------------------------------------------------------
diff --git a/docs/preprocessor/awk/init-code-blocks.awk b/docs/preprocessor/awk/init-code-blocks.awk
index 4b52acd..7065df8a 100644
--- a/docs/preprocessor/awk/init-code-blocks.awk
+++ b/docs/preprocessor/awk/init-code-blocks.awk
@@ -60,16 +60,13 @@ BEGIN {
     print "jython.eval('sys.path.append(\"" TP_HOME "/gremlin-python/target/test-classes/Lib\")')"
     print "jython.eval('from gremlin_python import statics')"
     print "jython.eval('from gremlin_python.process.traversal import *')"
-    print "jython.eval('from gremlin_python.process.graph_traversal import GraphTraversal')"
-    print "jython.eval('from gremlin_python.process.graph_traversal import GraphTraversalSource')"
-    print "jython.eval('from gremlin_python.process.graph_traversal import __')"
-    print "jython.eval('from gremlin_python.structure.remote_graph import RemoteGraph')"
+    print "jython.eval('from gremlin_python.structure.graph import Graph')"
     print "jython.eval('from gremlin_python.process.graphson import GraphSONWriter')"
     # print "jython.eval('from gremlin_python.process.graphson import serializers')"
     # print "jython.eval('from gremlin_python.driver.websocket_remote_connection import WebSocketRemoteConnection')"
     print "jython.eval('statics.load_statics(globals())')"
     print "jythonBindings = jython.createBindings()"
-    print "jythonBindings.put('g', jython.eval('RemoteGraph(None).traversal()'))"
+    print "jythonBindings.put('g', jython.eval('Graph().traversal()'))"
     print "jythonBindings.put('h', g)"
     print "jython.getContext().setBindings(jythonBindings, GLOBAL_SCOPE)"
     print "groovy = new org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine()"

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/docs/src/reference/gremlin-variants.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index dd1577a..0d4ab03 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -28,7 +28,7 @@ Gremlin in any modern programming language.
 IMPORTANT: Gremlin-Java is the canonical representation of Gremlin and any (proper) Gremlin language variant will emulate its
 structure as best as possible given the constructs of the host language. A strong correspondence between variants ensures
 that the general Gremlin reference documentation is applicable to all variants and that users moving between development
-languages can easily adopt the Gremlin language variant for that language.
+languages can easily adopt the Gremlin variant for that language.
 
 image::gremlin-variant-architecture.png[width=650,float=left]
 
@@ -48,26 +48,23 @@ link:https://en.wikipedia.org/wiki/CPython[CPython] machine. Python's syntax has
 namespaces (`a(b())` vs `a(__.b())`). As such, anyone familiar with Gremlin-Java will immediately be able to work
 with Gremlin-Python. Moreover, there are a few added constructs to Gremlin-Python that make traversals a bit more succinct.
 
-CAUTION: Python has `as`, `in`, `and`, `or`, `is`, `not`, `from`, and `global` as reserved words. Gremlin-Python simply
+CAUTION: In Python, `as`, `in`, `and`, `or`, `is`, `not`, `from`, and `global` are reserved words. Gremlin-Python simply
 prefixes `_` in front of these terms for their use with graph traversal. For instance: `g.V()._as('a')._in()._as('b').select('a','b')`.
 
-To install Gremlin-Python, simply use Python's link:https://en.wikipedia.org/wiki/Pip_(package_manager)[pip] package manager.
+To install Gremlin-Python, use Python's link:https://en.wikipedia.org/wiki/Pip_(package_manager)[pip] package manager.
 
 [source,bash]
 pip install gremlin_python
 
-There are three primary classes distributed with Gremlin-Python: `GraphTraversalSource`, `GraphTraversal`, and `__`.
+Gremlin-Python users will typically make use of the following classes.
 
 [source,python]
-from gremlin_python import statics
-from gremlin_python.structure.remote_graph import RemoteGraph
-from gremlin_python.process.graph_traversal import GraphTraversalSource
-from gremlin_python.process.graph_traversal import GraphTraversal
-from gremlin_python.process.graph_traversal import __
-from gremlin_python.driver.websocket_remote_connection import WebSocketRemoteConnection
-
-These classes mirror `GraphTraversalSource`, `GraphTraversal`, and `__`, respectively in Gremlin-Java. The `GraphTraversalSource`
-requires a driver in order to communicate with <<gremlin-server,GremlinServer>> (or any <<connecting-via-remotegraph,`RemoteConnection`>>-enabled server).
+>>> from gremlin_python import statics
+>>> from gremlin_python.structure.graph import Graph
+>>> from gremlin_python.driver.websocket_remote_connection import WebSocketRemoteConnection
+
+In Gremlin-Python 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 <<connecting-via-remotegraph,`RemoteConnection`>>-enabled server).
 The `gremlin_rest_driver` is provided with Apache TinkerPop and it serves as a simple (though verbose) driver that sends traversals to GremlinServer
 via HTTP POST (using link:http://docs.python-requests.org/[requests]) and in return, is provided <<graphson-reader-writer,GraphSON>>-encoded results.
 `WebSocketRemoteConnection` extends the abstract class `RemoteConnection` in `gremlin_python.driver`.
@@ -75,22 +72,26 @@ via HTTP POST (using link:http://docs.python-requests.org/[requests]) and in ret
 IMPORTANT: For developers wishing to provide another *driver implementation*, be sure to extend `RemoteConnection` in
 `gremlin_python.driver` so it can then be used by Gremlin-Python's `GraphTraversal`.
 
-When GremlinServer is running, Gremlin-Python can communicate with GremlinServer. The `conf/gremlin-server-rest.modern.yaml`
-configuration is used to expose GremlinServer's REST interface.
+When GremlinServer is running, Gremlin-Python can communicate with GremlinServer. The `conf/gremlin-server-modern-py.yaml`
+configuration maintains a `GremlinJythonScriptEngine` as well as the appropriate serializers for communicating `Bytecode`.
 
 [source,bash]
 ----
-$ bin/gremlin-server.sh conf/gremlin-server-rest-modern.yaml
+$ bin/gremlin-server.sh -i 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---
 
-[INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-rest-modern.yaml
+[INFO] GremlinServer - Configuring Gremlin Server from conf/gremlin-server-modern-py.yaml
 [INFO] GraphManager - Graph [graph] was successfully configured via [conf/tinkergraph-empty.properties].
+[INFO] ScriptEngines - Loaded gremlin-jython 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] TraversalOpProcessor - Initialized cache for TraversalOpProcessor with size 1000 and expiration time of 600000 ms
+[INFO] AbstractChannelizer - Configured application/vnd.gremlin-v1.0+json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0
 [INFO] AbstractChannelizer - Configured application/json with org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0
 [INFO] GremlinServer$1 - Channel started at port 8182.
 ----
@@ -98,14 +99,18 @@ $ bin/gremlin-server.sh conf/gremlin-server-rest-modern.yaml
 Within the CPython console, it is possible to evaluate the following.
 
 [source,python]
-graph = RemoteGraph(WebSocketsRemoteConnection('ws://localhost:8182','g'))
-g = graph.traversal()
+>>> graph = Graph()
+>>> g = graph.traversal().withRemote(WebSocketRemoteConnection('ws://localhost:8182','g'))
+
+When a traversal spawned from the `GraphTraversalSource` above is iterated, the traveral's `Bytecode` is sent over the wire
+via the registered `RemoteConnection`. 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. GremlinServer) 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`).
 
-When the traversal above is submitted to the `RemoteConnection`, it's `Bytecode` is sent in order to construct the equivalent traversal
-in GremlinServer (thus, remotely). The bytecode is analyzed to determine which language the bytecode should be translated to.
-If the traversal does not have lambdas, it 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`).
+IMPORTANT: Gremlin-Python's `Traversal` class supports the standard Gremlin methods such as `next()`, `nextTraverser()`,
+`toSet()`, `toList()`, etc. Such "terminal" methods trigger the evaluation of the traversal.
 
 Gremlin-Python Sugar
 ~~~~~~~~~~~~~~~~~~~~
@@ -126,16 +131,16 @@ Static Enums and Methods
 Gremlin has various tokens (e.g. `T`, `P`, `Order`, `Operator`, etc.) that are represented in Gremlin-Python as Python `Enums`.
 
 [source,python]
-from gremlin_python.process.traversal import T
-from gremlin_python.process.traversal import Order
-from gremlin_python.process.traversal import Cardinality
-from gremlin_python.process.traversal import Column
-from gremlin_python.process.traversal import Direction
-from gremlin_python.process.traversal import Operator
-from gremlin_python.process.traversal import P
-from gremlin_python.process.traversal import Pop
-from gremlin_python.process.traversal import Scope
-from gremlin_python.process.traversal import Barrier
+>>> from gremlin_python.process.traversal import T
+>>> from gremlin_python.process.traversal import Order
+>>> from gremlin_python.process.traversal import Cardinality
+>>> from gremlin_python.process.traversal import Column
+>>> from gremlin_python.process.traversal import Direction
+>>> from gremlin_python.process.traversal import Operator
+>>> from gremlin_python.process.traversal import P
+>>> from gremlin_python.process.traversal import Pop
+>>> from gremlin_python.process.traversal import Scope
+>>> from gremlin_python.process.traversal import Barrier
 
 These can be used analogously to how they are used in Gremlin-Java.
 
@@ -147,7 +152,7 @@ g.V().hasLabel('person').has('age',P.gt(30)).order().by('age',Order.decr).toList
 Moreover, by importing the `statics` of Gremlin-Python, the class prefixes can be removed.
 
 [source,python]
-statics.load_statics(globals())
+>>> statics.load_statics(globals())
 
 With statics loaded its possible to represent the above traversal as below.
 
@@ -167,11 +172,11 @@ g.V().repeat(out()).times(2).name.fold().toList()
 RemoteConnection Bindings
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-When a traversal bytecode is sent over a `RemoteConnection` (e.g. GremlinServer), it will be translated, compiled, and executed accordingly.
-If the same traversal is sent again, translation and compilation can be skipped as the previously compiled version is typically cached.
+When a traversal bytecode is sent over a `RemoteConnection` (e.g. GremlinServer), it will be translated, compiled, and then executed.
+If the same traversal is sent again, translation and compilation can be skipped as the previously compiled version should be cached.
 Many traversals are unique up to some parameterization. For instance, `g.V(1).out('created').name` is considered different
-from `g.V(4).out('created').name'` as they are different scripts. However, `g.V(x).out('created').name` with bindings of `{x : 1}` and
-`{x : 4}` are the same. If a traversal is going to be executed repeatedly, but with different parameters, then bindings should be used.
+from `g.V(4).out('created').name'` as they are different "string" scripts. However, `g.V(x).out('created').name` with bindings of `{x : 1}` and
+`{x : 4}` are considered the same. If a traversal is going to be executed repeatedly, but with different parameters, then bindings should be used.
 In Gremlin-Python, bindings are 2-tuples and used as follows.
 
 [gremlin-python,modern]
@@ -180,15 +185,18 @@ g.V(('id',1)).out('created').name.toList()
 g.V(('id',4)).out('created').name.toList()
 ----
 
+IMPORTANT: The Gremlin-Java `withBindings()` traversal source step is not needed. Gremlin-Java's model is only required
+in statically typed languages where bindings need to have the same typing as the `Traversal` API.
+
 The Lambda Solution
 ~~~~~~~~~~~~~~~~~~~
 
 Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is extremely difficult.
-As a simple solution, it is up to the Gremlin variant to decide lambdas (in any language) should be expressed and ultimately
-encoded in the standard `Bytecode` format. In Gremlin-Python, a link:https://docs.python.org/2/reference/expressions.html#lambda[Python lambda]
-should be a zero-arg callable that returns a string representation of a lambda. The default lambda language is `gremlin-python`
-and can be changed via `gremlin_python.statics.default_lambda_language.` When the lambda is represented in `Bytecode` its language is encoded
-such that the remote connection host can infer which translator to use.
+In Gremlin-Python, a link:https://docs.python.org/2/reference/expressions.html#lambda[Python lambda]
+should be represented as a zero-arg callable that returns a string representation of a lambda.
+The default lambda language is `gremlin-python` and can be changed via `gremlin_python.statics.default_lambda_language.`
+When the lambda is represented in `Bytecode` its language is encoded such that the remote connection host can infer
+which translator and ultimate execution engine to use.
 
 [gremlin-python,modern]
 ----

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalSource.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalSource.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalSource.java
index 5e82716..7c9557c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalSource.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalSource.java
@@ -93,6 +93,7 @@ public interface TraversalSource extends Cloneable {
         public static final String withoutStrategies = "withoutStrategies";
         public static final String withComputer = "withComputer";
         public static final String withSideEffect = "withSideEffect";
+        public static final String withRemote = "withRemote";
     }
 
     /////////////////////////////

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GraphTraversalSourceGenerator.groovy
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GraphTraversalSourceGenerator.groovy b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GraphTraversalSourceGenerator.groovy
index f076da9..4619c18 100644
--- a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GraphTraversalSourceGenerator.groovy
+++ b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/GraphTraversalSourceGenerator.groovy
@@ -57,7 +57,9 @@ under the License.
 '''
 """)
         pythonClass.append("from .traversal import Traversal\n")
+        pythonClass.append("from .traversal import TraversalStrategies\n")
         pythonClass.append("from .traversal import Bytecode\n")
+        pythonClass.append("from ..driver.remote_connection import RemoteStrategy\n")
         pythonClass.append("from .. import statics\n\n")
 
 //////////////////////////
@@ -75,7 +77,11 @@ under the License.
     return "graphtraversalsource[" + str(self.graph) + "]"
 """)
         GraphTraversalSource.getMethods()
-                .findAll { !it.name.equals("clone") && !it.name.equals(TraversalSource.Symbols.withBindings) }
+                .findAll {
+            !it.name.equals("clone") &&
+                    !it.name.equals(TraversalSource.Symbols.withBindings) &&
+                    !it.name.equals(TraversalSource.Symbols.withRemote)
+        }
                 .collect { it.name }
                 .unique()
                 .sort { a, b -> a <=> b }
@@ -96,13 +102,18 @@ under the License.
                 } else if (TraversalSource.isAssignableFrom(returnType)) {
                     pythonClass.append(
                             """  def ${method}(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("${method}", *args)
     return source
 """)
                 }
             }
         }
+        pythonClass.append("""  def withRemote(self, remote_connection):
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
+    source.traversal_strategies.add_strategies([RemoteStrategy(remote_connection)])
+    return source
+""")
         pythonClass.append("\n\n")
 
 ////////////////////

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy
index 9845595..65dcabc 100644
--- a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy
+++ b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy
@@ -192,10 +192,13 @@ TRAVERSAL STRATEGIES
 class TraversalStrategies(object):
     global_cache = {}
 
-    def __init__(self, traversal_strategies):
-        self.traversal_strategies = traversal_strategies
+    def __init__(self, traversal_strategies=None):
+        self.traversal_strategies = traversal_strategies.traversal_strategies if traversal_strategies is not None else []
         return
 
+    def add_strategies(self, traversal_strategies):
+        self.traversal_strategies = self.traversal_strategies + traversal_strategies
+
     def apply_strategies(self, traversal):
         for traversal_strategy in self.traversal_strategies:
             traversal_strategy.apply(traversal)

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/driver/__init__.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/driver/__init__.py b/gremlin-python/src/main/jython/gremlin_python/driver/__init__.py
index c9f1586..4f06e4e 100644
--- a/gremlin-python/src/main/jython/gremlin_python/driver/__init__.py
+++ b/gremlin-python/src/main/jython/gremlin_python/driver/__init__.py
@@ -17,7 +17,12 @@ specific language governing permissions and limitations
 under the License.
 '''
 from .remote_connection import RemoteConnection
+from .remote_connection import RemoteStrategy
 from .remote_connection import RemoteTraversal
-from .websocket_remote_connection import WebSocketRemoteConnection
+
+try:
+    from .websocket_remote_connection import WebSocketRemoteConnection
+except ImportError:
+    pass
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/driver/remote_connection.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/driver/remote_connection.py b/gremlin-python/src/main/jython/gremlin_python/driver/remote_connection.py
index fffa677..43fed53 100644
--- a/gremlin-python/src/main/jython/gremlin_python/driver/remote_connection.py
+++ b/gremlin-python/src/main/jython/gremlin_python/driver/remote_connection.py
@@ -20,6 +20,7 @@ import abc
 import six
 
 from ..process.traversal import Traversal
+from ..process.traversal import TraversalStrategy
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 
@@ -49,3 +50,15 @@ class RemoteTraversal(Traversal):
         Traversal.__init__(self, None, None, None)
         self.traversers = traversers
         self.side_effects = side_effects
+
+
+class RemoteStrategy(TraversalStrategy):
+    def __init__(self, remote_connection):
+        self.remote_connection = remote_connection
+
+    def apply(self, traversal):
+        if traversal.traversers is None:
+            remote_traversal = self.remote_connection.submit(traversal.bytecode)
+            traversal.side_effects = remote_traversal.side_effects
+            traversal.traversers = remote_traversal.traversers
+        return

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
index 43d9b91..8b3ff88 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/graph_traversal.py
@@ -17,7 +17,9 @@ specific language governing permissions and limitations
 under the License.
 '''
 from .traversal import Traversal
+from .traversal import TraversalStrategies
 from .traversal import Bytecode
+from ..driver.remote_connection import RemoteStrategy
 from .. import statics
 
 class GraphTraversalSource(object):
@@ -46,37 +48,37 @@ class GraphTraversalSource(object):
     traversal.bytecode.add_step("inject", *args)
     return traversal
   def withBulk(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withBulk", *args)
     return source
   def withComputer(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withComputer", *args)
     return source
   def withPath(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withPath", *args)
     return source
-  def withRemote(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
-    source.bytecode.add_source("withRemote", *args)
-    return source
   def withSack(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withSack", *args)
     return source
   def withSideEffect(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withSideEffect", *args)
     return source
   def withStrategies(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withStrategies", *args)
     return source
   def withoutStrategies(self, *args):
-    source = GraphTraversalSource(self.graph, self.traversal_strategies, Bytecode(self.bytecode))
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
     source.bytecode.add_source("withoutStrategies", *args)
     return source
+  def withRemote(self, remote_connection):
+    source = GraphTraversalSource(self.graph, TraversalStrategies(self.traversal_strategies), Bytecode(self.bytecode))
+    source.traversal_strategies.add_strategies([RemoteStrategy(remote_connection)])
+    return source
 
 
 class GraphTraversal(Traversal):

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
index 46b7faa..3347034 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
@@ -272,10 +272,13 @@ TRAVERSAL STRATEGIES
 class TraversalStrategies(object):
     global_cache = {}
 
-    def __init__(self, traversal_strategies):
-        self.traversal_strategies = traversal_strategies
+    def __init__(self, traversal_strategies=None):
+        self.traversal_strategies = traversal_strategies.traversal_strategies if traversal_strategies is not None else []
         return
 
+    def add_strategies(self, traversal_strategies):
+        self.traversal_strategies = self.traversal_strategies + traversal_strategies
+
     def apply_strategies(self, traversal):
         for traversal_strategy in self.traversal_strategies:
             traversal_strategy.apply(traversal)

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/structure/__init__.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/__init__.py b/gremlin-python/src/main/jython/gremlin_python/structure/__init__.py
index 1c69b5c..cb2d3b6 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/__init__.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/__init__.py
@@ -17,6 +17,5 @@ specific language governing permissions and limitations
 under the License.
 '''
 from .graph import Graph
-from .remote_graph import RemoteGraph
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/structure/graph.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/graph.py b/gremlin-python/src/main/jython/gremlin_python/structure/graph.py
index 60b2211..b451ec6 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/graph.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/graph.py
@@ -24,5 +24,11 @@ from gremlin_python.process.traversal import TraversalStrategies
 
 
 class Graph(object):
+    def __init__(self):
+        TraversalStrategies.global_cache[self.__class__] = TraversalStrategies()
+
     def traversal(self):
         return GraphTraversalSource(self, TraversalStrategies.global_cache[self.__class__])
+
+    def __repr__(self):
+        return "graph[]"

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/main/jython/gremlin_python/structure/remote_graph.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/remote_graph.py b/gremlin-python/src/main/jython/gremlin_python/structure/remote_graph.py
deleted file mode 100644
index fa2fdb1..0000000
--- a/gremlin-python/src/main/jython/gremlin_python/structure/remote_graph.py
+++ /dev/null
@@ -1,45 +0,0 @@
-'''
-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.
-'''
-
-__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
-
-from gremlin_python.process.traversal import TraversalStrategies
-from gremlin_python.process.traversal import TraversalStrategy
-from .graph import Graph
-
-
-class RemoteGraph(Graph):
-    def __init__(self, remote_connection):
-        TraversalStrategies.global_cache[self.__class__] = TraversalStrategies([RemoteStrategy()])
-        self.remote_connection = remote_connection
-
-    def __repr__(self):
-        return "remotegraph[" + self.remote_connection.url + "]"
-
-
-class RemoteStrategy(TraversalStrategy):
-    def apply(self, traversal):
-        if not (traversal.graph.__class__.__name__ == "RemoteGraph"):
-            raise BaseException(
-                "RemoteStrategy can only be used with a RemoteGraph: " + traversal.graph.__class__.__name__)
-        if traversal.traversers is None:
-            remote_traversal = traversal.graph.remote_connection.submit(traversal.bytecode)
-            traversal.side_effects = remote_traversal.side_effects
-            traversal.traversers = remote_traversal.traversers
-        return

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/driver/WebSocketRemoteConnectionTest.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/driver/WebSocketRemoteConnectionTest.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/driver/WebSocketRemoteConnectionTest.java
index 434056d..aa595c3 100644
--- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/driver/WebSocketRemoteConnectionTest.java
+++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/driver/WebSocketRemoteConnectionTest.java
@@ -49,9 +49,9 @@ public class WebSocketRemoteConnectionTest {
         try {
             JythonScriptEngineSetup.setup();
             jython.getContext().getBindings(ScriptContext.ENGINE_SCOPE)
-                    .put("g", jython.eval("RemoteGraph(WebSocketRemoteConnection('ws://localhost:8182','g')).traversal()"));
+                    .put("g", jython.eval("Graph().traversal().withRemote(WebSocketRemoteConnection('ws://localhost:8182','g'))"));
             jython.getContext().getBindings(ScriptContext.ENGINE_SCOPE)
-                    .put("j", jython.eval("RemoteGraph(WebSocketRemoteConnection('ws://localhost:8182','g')).traversal()"));
+                    .put("j", jython.eval("Graph().traversal().withRemote(WebSocketRemoteConnection('ws://localhost:8182','g'))"));
             new GremlinServer(Settings.read(WebSocketRemoteConnectionTest.class.getResourceAsStream("gremlin-server-rest-modern.yaml"))).start().join();
         } catch (final Exception ex) {
             ex.printStackTrace();

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java
index 618c488..75e9ce0 100644
--- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java
+++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java
@@ -45,7 +45,7 @@ public class JythonScriptEngineSetup {
             jythonEngine.eval("from gremlin_python.process.graph_traversal import __");
             // jythonEngine.eval("from gremlin_python.driver.websocket_remote_connection import WebSocketRemoteConnection");
             jythonEngine.eval("from gremlin_python.process.traversal import Bytecode");
-            jythonEngine.eval("from gremlin_python.structure.remote_graph import RemoteGraph");
+            jythonEngine.eval("from gremlin_python.structure.graph import Graph");
             jythonEngine.eval("from gremlin_python.process.graphson import GraphSONWriter");
         } catch (final ScriptException e) {
             throw new IllegalStateException(e.getMessage(), e);

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java
index 2a3e2b9..9f46bea 100644
--- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java
+++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java
@@ -62,7 +62,7 @@ final class PythonGraphSONJavaTranslator<S extends TraversalSource, T extends Tr
             final ScriptEngine jythonEngine = ScriptEngineCache.get("jython");
             final Bindings bindings = jythonEngine.createBindings();
             bindings.putAll(jythonEngine.getBindings(ScriptContext.ENGINE_SCOPE));
-            bindings.put(this.pythonTranslator.getTraversalSource(), jythonEngine.eval("RemoteGraph(None).traversal()"));
+            bindings.put(this.pythonTranslator.getTraversalSource(), jythonEngine.eval("Graph().traversal()"));
             bindings.putAll(bytecode.getBindings());
             final String graphsonBytecode = jythonEngine.eval("GraphSONWriter.writeObject(" + this.pythonTranslator.translate(bytecode) + ")", bindings).toString();
             return this.javaTranslator.translate(this.reader.readObject(new ByteArrayInputStream(graphsonBytecode.getBytes()), Bytecode.class));

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/4a664551/gremlin-server/conf/gremlin-server-modern-py.yaml
----------------------------------------------------------------------
diff --git a/gremlin-server/conf/gremlin-server-modern-py.yaml b/gremlin-server/conf/gremlin-server-modern-py.yaml
index 44f7fe4..5c7a9c5 100644
--- a/gremlin-server/conf/gremlin-server-modern-py.yaml
+++ b/gremlin-server/conf/gremlin-server-modern-py.yaml
@@ -39,7 +39,9 @@ scriptEngines: {
     imports: [java.lang.Math],
     staticImports: [java.lang.Math.PI],
     scripts: [scripts/generate-modern.groovy]},
-  gremlin-jython: {}}
+  gremlin-jython: {},
+  gremlin-python: {}
+}
 serializers:
   - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { useMapperFromGraph: graph }}            # application/vnd.gremlin-v1.0+gryo
   - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: { useMapperFromGraph: graph }}        # application/vnd.gremlin-v1.0+gryo-lite