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/08/13 14:50:57 UTC

[tinkerpop] 23/24: Major refactoring for nullable writes.

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

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

commit 88060f8a0ea39bba05b5af7c94ee21401607a3ad
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Fri Aug 9 14:11:13 2019 -0400

    Major refactoring for nullable writes.
    
    Added more serializer support with metrics, class, and strategies. Still a bit rough but more tests are passing.
---
 gremlin-python/pom.xml                             |   9 +-
 .../jython/gremlin_python/driver/serializer.py     |   2 +-
 .../jython/gremlin_python/process/strategies.py    |   5 +-
 .../jython/gremlin_python/process/traversal.py     |   3 +-
 .../src/main/jython/gremlin_python/statics.py      |  15 +-
 .../gremlin_python/structure/io/graphbinaryV1.py   | 272 ++++++++++++++-------
 .../gremlin_python/structure/io/graphsonV3d0.py    |   1 +
 gremlin-python/src/main/jython/radish/terrain.py   |   2 +
 gremlin-python/src/main/jython/tests/conftest.py   |   6 +-
 .../tests/driver/test_driver_remote_connection.py  | 129 +---------
 10 files changed, 211 insertions(+), 233 deletions(-)

diff --git a/gremlin-python/pom.xml b/gremlin-python/pom.xml
index f3c3c85..48a45b0 100644
--- a/gremlin-python/pom.xml
+++ b/gremlin-python/pom.xml
@@ -419,13 +419,20 @@ limitations under the License.
                                             <env key="PYTHONPATH" value=""/>
                                             <arg line="setup.py install"/>
                                         </exec>
-                                        <!-- run for graphson 3.0 -->
+                                        <!-- run for graphson 2.0 -->
                                         <exec executable="env/bin/radish" dir="${project.build.directory}/python2"
                                               failonerror="true">
                                             <env key="PYTHONPATH" value=""/>
                                             <env key="PYTHONIOENCODING" value="utf-8:surrogateescape"/>
                                             <arg line="-f dots -e -t -b ${project.build.directory}/python2/radish ${project.basedir}/../gremlin-test/features/ --user-data=&quot;serializer=application/vnd.gremlin-v3.0+json&quot;"/> <!-- -no-line-jump -->
                                         </exec>
+                                        <!-- run for graphbinary 1.0 -->
+                                        <exec executable="env/bin/radish" dir="${project.build.directory}/python2"
+                                              failonerror="true">
+                                            <env key="PYTHONPATH" value=""/>
+                                            <env key="PYTHONIOENCODING" value="utf-8:surrogateescape"/>
+                                            <arg line="-f dots -e -t -b ${project.build.directory}/python2/radish ${project.basedir}/../gremlin-test/features/ --user-data=&quot;serializer=application/vnd.graphbinary-v1.0&quot;"/> <!-- -no-line-jump -->
+                                        </exec>
                                     </target>
                                 </configuration>
                             </execution>
diff --git a/gremlin-python/src/main/jython/gremlin_python/driver/serializer.py b/gremlin-python/src/main/jython/gremlin_python/driver/serializer.py
index f805220..a95d292 100644
--- a/gremlin-python/src/main/jython/gremlin_python/driver/serializer.py
+++ b/gremlin-python/src/main/jython/gremlin_python/driver/serializer.py
@@ -170,7 +170,7 @@ class GraphSONSerializersV3d0(GraphSONMessageSerializer):
         super(GraphSONSerializersV3d0, self).__init__(reader, writer, version)
 
 
-class GraphBinaryMessageSerializerV1(object):
+class GraphBinarySerializersV1(object):
     DEFAULT_READER_CLASS = graphbinaryV1.GraphBinaryReader
     DEFAULT_WRITER_CLASS = graphbinaryV1.GraphBinaryWriter
     DEFAULT_VERSION = b"application/vnd.graphbinary-v1.0"
diff --git a/gremlin-python/src/main/jython/gremlin_python/process/strategies.py b/gremlin-python/src/main/jython/gremlin_python/process/strategies.py
index cbd5e08..186e303 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/strategies.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/strategies.py
@@ -64,8 +64,9 @@ class PartitionStrategy(TraversalStrategy):
 
 
 class SubgraphStrategy(TraversalStrategy):
+    
     def __init__(self, vertices=None, edges=None, vertex_properties=None):
-        TraversalStrategy.__init__(self)
+        TraversalStrategy.__init__(self, fqcn="org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy")
         if vertices is not None:
             self.configuration["vertices"] = vertices
         if edges is not None:
@@ -77,7 +78,7 @@ class SubgraphStrategy(TraversalStrategy):
 class VertexProgramStrategy(TraversalStrategy):
     def __init__(self, graph_computer=None, workers=None, persist=None, result=None, vertices=None, edges=None,
                  configuration=None):
-        TraversalStrategy.__init__(self)
+        TraversalStrategy.__init__(self, fqcn="org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy")
         if graph_computer is not None:
             self.configuration["graphComputer"] = graph_computer
         if workers is not None:
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 4fb9f8c..5a73702 100644
--- a/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
+++ b/gremlin-python/src/main/jython/gremlin_python/process/traversal.py
@@ -581,8 +581,9 @@ class TraversalStrategies(object):
 
 
 class TraversalStrategy(object):
-    def __init__(self, strategy_name=None, configuration=None):
+    def __init__(self, strategy_name=None, configuration=None, fqcn=None):
         self.strategy_name = type(self).__name__ if strategy_name is None else strategy_name
+        self.fqcn = fqcn
         self.configuration = {} if configuration is None else configuration
 
     def apply(self, traversal):
diff --git a/gremlin-python/src/main/jython/gremlin_python/statics.py b/gremlin-python/src/main/jython/gremlin_python/statics.py
index 2f06b97..012f52f 100644
--- a/gremlin-python/src/main/jython/gremlin_python/statics.py
+++ b/gremlin-python/src/main/jython/gremlin_python/statics.py
@@ -43,19 +43,20 @@ else:
     from types import TypeType
     from types import ListType
     from types import DictType
+    from types import TypeType
 
 
 class timestamp(float):
     """
     In Python a timestamp is simply a float. This dummy class (similar to long), allows users to wrap a float
-    in a GLV script to make sure the value is serialized as a GraphSON timestamp.
+    in a GLV script to make sure the value is serialized as a Gremlin timestamp.
     """
     pass
 
 
 class SingleByte(int):
     """
-    Provides a way to pass a single byte via GraphSON.
+    Provides a way to pass a single byte via Gremlin.
     """
     def __new__(cls, b):
         if -128 <= b < 128:
@@ -66,7 +67,7 @@ class SingleByte(int):
 
 class SingleChar(str):
     """
-    Provides a way to pass a single character via GraphSON.
+    Provides a way to pass a single character via Gremlin.
     """
     def __new__(cls, c):
         if len(b) == 1:
@@ -75,6 +76,14 @@ class SingleChar(str):
             raise ValueError("string must contain a single character")
 
 
+class GremlinType(object):
+    """
+    Provides a way to represent a "Java class" for Gremlin.
+    """
+    def __init__(self, gremlin_type):
+        self.gremlin_type = gremlin_type
+        
+
 staticMethods = {}
 staticEnums = {}
 default_lambda_language = "gremlin-python"
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py
index f34a3d4..dd829cf 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphbinaryV1.py
@@ -36,7 +36,7 @@ from isodate import parse_duration, duration_isoformat
 
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType, DictType, ListType, SetType, \
-                                   SingleByte, ByteBufferType, SingleChar
+                                   SingleByte, ByteBufferType, SingleChar, GremlinType
 from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Cardinality, Column, Direction, Operator, \
                                              Order, Pick, Pop, P, Scope, TextP, Traversal, Traverser, \
                                              TraversalStrategy, T
@@ -58,7 +58,7 @@ class DataType(Enum):
     string = 0x03
     date = 0x04
     timestamp = 0x05
-    clazz = 0x06                  #todo
+    clazz = 0x06
     double = 0x07
     float = 0x08
     list = 0x09
@@ -68,7 +68,7 @@ class DataType(Enum):
     edge = 0x0d
     path = 0x0e
     property = 0x0f
-    graph = 0x10
+    graph = 0x10                  # no graph object in python yet
     vertex = 0x11
     vertexproperty = 0x12
     barrier = 0x13
@@ -93,14 +93,17 @@ class DataType(Enum):
     short = 0x26                  #todo?
     boolean = 0x27
     textp = 0x28
-    traversalstrategy = 0x29      #todo
+    traversalstrategy = 0x29
     bulkset = 0x2a
-    tree = 0x2b                   #todo
-    metrics = 0x2c                #todo
-    traversalmetrics = 0x2d       #todo
+    tree = 0x2b                   # no tree object in Python yet
+    metrics = 0x2c
+    traversalmetrics = 0x2d
     custom = 0x00                 #todo
 
 
+NULL_BYTES = [DataType.null.value, 0x01]
+
+
 class GraphBinaryTypeType(type):
     def __new__(mcs, name, bases, dct):
         cls = super(GraphBinaryTypeType, mcs).__new__(mcs, name, bases, dct)
@@ -172,11 +175,11 @@ class _GraphBinaryTypeIO(object):
                  "filter_": "filter", "id_": "id", "max_": "max", "min_": "min", "sum_": "sum"}
 
     @classmethod
-    def as_bytes(cls, graphbin_type=None, size=None, *args):
+    def as_bytes(cls, graphbin_type=None, size=None, nullable=True, *args):
         ba = bytearray() if graphbin_type is None else bytearray([graphbin_type.value])
 
-        # todo: empty value flag just hardcoded in
-        ba.extend(struct.pack(">b", 0))
+        if nullable:
+            ba.extend(struct.pack(">b", 0))
 
         if size is not None:
             ba.extend(struct.pack(">i", size))
@@ -215,7 +218,7 @@ class _GraphBinaryTypeIO(object):
     def is_null(cls, buff, reader, else_opt, nullable=True):
         return None if nullable and buff.read(1)[0] == 0x01 else else_opt(buff, reader)
 
-    def dictify(self, obj, writer, as_value=False):
+    def dictify(self, obj, writer, as_value=False, nullable=True):
         raise NotImplementedError()
 
     def objectify(self, d, reader, nullable=True):
@@ -229,12 +232,12 @@ class LongIO(_GraphBinaryTypeIO):
     byte_format = ">q"
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         if obj < -9223372036854775808 or obj > 9223372036854775807:
             raise Exception("TODO: don't forget bigint")
         else:
             return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), 
-                                None, struct.pack(cls.byte_format, obj))
+                                None, nullable, struct.pack(cls.byte_format, obj))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -258,7 +261,7 @@ class DateIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.date
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         try:
             timestamp_seconds = calendar.timegm(obj.utctimetuple())
             pts = timestamp_seconds * 1e3 + getattr(obj, 'microsecond', 0) / 1e3
@@ -267,7 +270,7 @@ class DateIO(_GraphBinaryTypeIO):
 
         ts = int(round(pts))
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            None, struct.pack(">q", ts))
+                            None, nullable, struct.pack(">q", ts))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -283,18 +286,18 @@ class TimestampIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.timestamp
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         # Java timestamp expects milliseconds integer - Have to use int because of legacy Python
         ts = int(round(obj * 1000))
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            None, struct.pack(">q", ts))
+                            None, nullable, struct.pack(">q", ts))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
         # Python timestamp expects seconds
         return cls.is_null(buff, reader, lambda b, r: statics.timestamp(struct.unpack(">q", b.read(8))[0] / 1000.0),
                            nullable)
-
+    
 
 def _long_bits_to_double(bits):
     return struct.unpack('d', struct.pack('Q', bits))[0]
@@ -313,19 +316,19 @@ class FloatIO(LongIO):
     byte_format = ">f"
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         if math.isnan(obj):
             return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                                None, struct.pack(cls.byte_format, NAN))
+                                None, nullable, struct.pack(cls.byte_format, NAN))
         elif math.isinf(obj) and obj > 0:
             return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                                None, struct.pack(cls.byte_format, POSITIVE_INFINITY))
+                                None, nullable, struct.pack(cls.byte_format, POSITIVE_INFINITY))
         elif math.isinf(obj) and obj < 0:
             return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                                None, struct.pack(cls.byte_format, NEGATIVE_INFINITY))
+                                None, nullable, struct.pack(cls.byte_format, NEGATIVE_INFINITY))
         else:
             return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                                None, struct.pack(cls.byte_format, obj))
+                                None, nullable, struct.pack(cls.byte_format, obj))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -346,23 +349,15 @@ class DoubleIO(FloatIO):
         return cls.is_null(buff, reader, lambda b, r: struct.unpack(cls.byte_format, b.read(8))[0], nullable)
 
 
-class TypeSerializer(_GraphBinaryTypeIO):
-    python_type = TypeType
-
-    @classmethod
-    def dictify(cls, typ, writer, as_value=False):
-        return writer.toDict(typ())
-
-
 class StringIO(_GraphBinaryTypeIO):
 
     python_type = str
     graphbinary_type = DataType.string
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            len(obj), obj.encode("utf-8"))
+                            len(obj), nullable, obj.encode("utf-8"))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -375,13 +370,13 @@ class ListIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.list
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         list_data = bytearray()
         for item in obj:
             list_data.extend(writer.writeObject(item))
 
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            len(obj), list_data)
+                            len(obj), nullable, list_data)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -414,14 +409,14 @@ class MapIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.map
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         map_data = bytearray()
         for k, v in obj.items():
             map_data.extend(writer.writeObject(k))
             map_data.extend(writer.writeObject(v))
 
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            len(obj), map_data)
+                            len(obj), nullable, map_data)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -446,9 +441,9 @@ class UuidIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.uuid
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value),
-                            None, obj.bytes)
+                            None, nullable, obj.bytes)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -461,7 +456,7 @@ class EdgeIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.edge
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(writer.writeObject(obj.id))
         ba.extend(cls.string_as_bytes(obj.label))
@@ -469,9 +464,9 @@ class EdgeIO(_GraphBinaryTypeIO):
         ba.extend(cls.string_as_bytes(obj.inV.label))
         ba.extend(writer.writeObject(obj.outV.id))
         ba.extend(cls.string_as_bytes(obj.outV.label))
-        ba.extend([DataType.null.value])
-        ba.extend([DataType.null.value])
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        ba.extend(NULL_BYTES)
+        ba.extend(NULL_BYTES)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -483,7 +478,7 @@ class EdgeIO(_GraphBinaryTypeIO):
         edgelbl = cls.read_string(b)
         edge = Edge(edgeid, Vertex(r.readObject(b), cls.read_string(b)),
                     edgelbl, Vertex(r.readObject(b), cls.read_string(b)))
-        b.read(2)
+        b.read(4)
         return edge
 
 
@@ -493,11 +488,11 @@ class PathIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.path
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(writer.writeObject(obj.labels))
         ba.extend(writer.writeObject(obj.objects))
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -510,12 +505,12 @@ class PropertyIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.property
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(cls.string_as_bytes(obj.key))
         ba.extend(writer.writeObject(obj.value))
-        ba.extend([DataType.null.value])
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        ba.extend(NULL_BYTES)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -524,7 +519,7 @@ class PropertyIO(_GraphBinaryTypeIO):
     @classmethod
     def _read_property(cls, b, r):
         p = Property(cls.read_string(b), r.readObject(b), None)
-        b.read(1)
+        b.read(2)
         return p
 
 
@@ -534,7 +529,7 @@ class TinkerGraphIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.graph
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         raise AttributeError("TinkerGraph serialization is not currently supported by gremlin-python")
 
     @classmethod
@@ -548,12 +543,12 @@ class VertexIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.vertex
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(writer.writeObject(obj.id))
         ba.extend(cls.string_as_bytes(obj.label))
-        ba.extend([DataType.null.value])
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        ba.extend(NULL_BYTES)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -562,7 +557,7 @@ class VertexIO(_GraphBinaryTypeIO):
     @classmethod
     def _read_vertex(cls, b, r):
         vertex = Vertex(r.readObject(b), cls.read_string(b))
-        b.read(1)
+        b.read(2)
         return vertex
 
 
@@ -572,14 +567,14 @@ class VertexPropertyIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.vertexproperty
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(writer.writeObject(obj.id))
         ba.extend(cls.string_as_bytes(obj.label))
         ba.extend(writer.writeObject(obj.value))
-        ba.extend([DataType.null.value])
-        ba.extend([DataType.null.value])
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        ba.extend(NULL_BYTES)
+        ba.extend(NULL_BYTES)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -588,18 +583,17 @@ class VertexPropertyIO(_GraphBinaryTypeIO):
     @classmethod
     def _read_vertexproperty(cls, b, r):
         vp = VertexProperty(r.readObject(b), cls.read_string(b), r.readObject(b), None)
-        b.read(1)
-        b.read(1)
+        b.read(4)
         return vp
 
 
 class _EnumIO(_GraphBinaryTypeIO):
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(cls.string_as_bytes(cls.unmangleKeyword(str(obj.name))))
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -652,11 +646,11 @@ class BindingIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.binding
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(cls.string_as_bytes(obj.key))
         ba.extend(writer.writeObject(obj.value))
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -668,7 +662,7 @@ class BytecodeIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.bytecode
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(struct.pack(">i", len(obj.step_instructions)))
         for inst in obj.step_instructions:
@@ -684,9 +678,12 @@ class BytecodeIO(_GraphBinaryTypeIO):
             ba.extend(cls.string_as_bytes(inst_name))
             ba.extend(struct.pack(">i", len(inst_args)))
             for arg in inst_args:
-                ba.extend(writer.writeObject(arg))
+                if isinstance(arg, TypeType):
+                    ba.extend(writer.writeObject(GremlinType(arg().fqcn)))
+                else:
+                    ba.extend(writer.writeObject(arg))
 
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -729,7 +726,7 @@ class LambdaIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.lambda_
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray() if as_value else bytearray([cls.graphbinary_type.value])
         lambda_result = obj()
         script = lambda_result if isinstance(lambda_result, str) else lambda_result[0]
@@ -748,7 +745,7 @@ class LambdaIO(_GraphBinaryTypeIO):
         ba.extend(cls.string_as_bytes(script_cleaned))
         ba.extend(struct.pack(">i", script_args))
 
-        return ba
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
 
 class PIO(_GraphBinaryTypeIO):
@@ -756,16 +753,25 @@ class PIO(_GraphBinaryTypeIO):
     python_type = P
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
-        ba = bytearray() if as_value else bytearray([cls.graphbinary_type.value])
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        ba = bytearray()
         ba.extend(cls.string_as_bytes(obj.operator))
-        additional = [writer.writeObject(obj.value), writer.writeObject(obj.other)] \
-            if obj.other is not None else [writer.writeObject(obj.value)]
-        ba.extend(struct.pack(">i", len(additional)))
-        for a in additional:
-            ba.extend(a)
+        
+        args = []
+        if obj.other is None:
+            if isinstance(obj.value, ListType):
+                args = obj.value
+            else:
+                args.append(obj.value)
+        else:
+            args.append(obj.value)
+            args.append(obj.other)
+        
+        ba.extend(struct.pack(">i", len(args)))
+        for a in args:
+            ba.extend(writer.writeObject(a))
 
-        return ba
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
 
 class ScopeIO(_EnumIO):
@@ -783,11 +789,11 @@ class TraverserIO(_GraphBinaryTypeIO):
     python_type = Traverser
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         ba = bytearray()
         ba.extend(struct.pack(">q", obj.bulk))
         ba.extend(writer.writeObject(obj.object))
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, ba)
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -805,8 +811,8 @@ class ByteIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.byte
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, struct.pack(">b", obj))
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, struct.pack(">b", obj))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -820,8 +826,8 @@ class ByteBufferIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.bytebuffer
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
-        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), len(obj), obj)
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), len(obj), nullable, obj)
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -838,9 +844,9 @@ class BooleanIO(_GraphBinaryTypeIO):
     graphbinary_type = DataType.boolean
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
         return cls.as_bytes(cls.write_as_value(
-            cls.graphbinary_type, as_value), None, struct.pack(">b", 0x01 if obj else 0x00))
+            cls.graphbinary_type, as_value), None, nullable, struct.pack(">b", 0x01 if obj else 0x00))
 
     @classmethod
     def objectify(cls, buff, reader, nullable=True):
@@ -854,16 +860,25 @@ class TextPIO(_GraphBinaryTypeIO):
     python_type = TextP
 
     @classmethod
-    def dictify(cls, obj, writer, as_value=False):
-        ba = bytearray() if as_value else bytearray([cls.graphbinary_type.value])
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        ba = bytearray()
         ba.extend(cls.string_as_bytes(obj.operator))
-        additional = [writer.writeObject(obj.value), writer.writeObject(obj.other)] \
-            if obj.other is not None else [writer.writeObject(obj.value)]
-        ba.extend(struct.pack(">i", len(additional)))
-        for a in additional:
-            ba.extend(a)
 
-        return ba
+        args = []
+        if obj.other is None:
+            if isinstance(obj.value, ListType):
+                args = obj.value
+            else:
+                args.append(obj.value)
+        else:
+            args.append(obj.value)
+            args.append(obj.other)
+
+        ba.extend(struct.pack(">i", len(args)))
+        for a in args:
+            ba.extend(writer.writeObject(a))
+
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
 
 
 class BulkSetIO(_GraphBinaryTypeIO):
@@ -886,3 +901,72 @@ class BulkSetIO(_GraphBinaryTypeIO):
             size = size - 1
 
         return the_list
+
+
+class MetricsIO(_GraphBinaryTypeIO):
+
+    graphbinary_type = DataType.metrics
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_metrics, nullable)
+
+    @classmethod
+    def _read_metrics(cls, b, r):
+        metricid = cls.read_string(b)
+        name = cls.read_string(b)
+        duration = r.toObject(b, DataType.long, nullable=False)
+        counts = r.toObject(b, DataType.map, nullable=False)
+        annotations = r.toObject(b, DataType.map, nullable=False)
+        metrics = r.toObject(b, DataType.list, nullable=False)
+
+        return {"id": metricid,
+                "name": name,
+                "dur": duration,
+                "counts": counts,
+                "annotations": annotations,
+                "metrics": metrics}
+
+
+class TraversalMetricsIO(_GraphBinaryTypeIO):
+
+    graphbinary_type = DataType.traversalmetrics
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_traversalmetrics, nullable)
+
+    @classmethod
+    def _read_traversalmetrics(cls, b, r):
+        duration = r.toObject(b, DataType.long, nullable=False)
+        metrics = r.toObject(b, DataType.list, nullable=False)
+
+        return {"dur": duration,
+                "metrics": metrics}
+
+
+class ClassIO(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.clazz
+    python_type = GremlinType
+
+    @classmethod
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        return cls.as_bytes(cls.write_as_value(
+            cls.graphbinary_type, as_value), None, nullable, StringIO.dictify(obj.gremlin_type, writer, True, False))
+
+
+class TraversalStrategyIO(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.traversalstrategy
+    python_type = TraversalStrategy
+
+    @classmethod
+    def dictify(cls, obj, writer, as_value=False, nullable=True):
+        ba = bytearray()
+        ba.extend(ClassIO.dictify(GremlinType(obj.fqcn), writer, True, False))
+        conf = {k: cls._convert(v) for k, v in obj.configuration.items()}
+        ba.extend(MapIO.dictify(conf, writer, True, False))
+        return cls.as_bytes(cls.write_as_value(cls.graphbinary_type, as_value), None, nullable, ba)
+
+    @classmethod
+    def _convert(cls, v):
+        return v.bytecode if isinstance(v, Traversal) else v
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
index 8c3cd9f..10a74da 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphsonV3d0.py
@@ -711,6 +711,7 @@ class TDeserializer(_GraphSONTypeIO):
     def objectify(cls, d, reader):
         return T[d]
 
+
 class TraversalMetricsDeserializer(_GraphSONTypeIO):
     graphson_type = "g:TraversalMetrics"
 
diff --git a/gremlin-python/src/main/jython/radish/terrain.py b/gremlin-python/src/main/jython/radish/terrain.py
index 9122eac..245fd9b 100644
--- a/gremlin-python/src/main/jython/radish/terrain.py
+++ b/gremlin-python/src/main/jython/radish/terrain.py
@@ -89,6 +89,8 @@ def __create_remote(server_graph_name):
 
     if world.config.user_data["serializer"] == "application/vnd.gremlin-v3.0+json":
         s = serializer.GraphSONSerializersV3d0()
+    elif world.config.user_data["serializer"] == "application/vnd.graphbinary-v1.0":
+        s = serializer.GraphBinarySerializersV1()
     else:
         raise ValueError('serializer not found - ' + world.config.user_data["serializer"])
 
diff --git a/gremlin-python/src/main/jython/tests/conftest.py b/gremlin-python/src/main/jython/tests/conftest.py
index fb31c31..9597f4d 100644
--- a/gremlin-python/src/main/jython/tests/conftest.py
+++ b/gremlin-python/src/main/jython/tests/conftest.py
@@ -29,7 +29,7 @@ from gremlin_python.driver.driver_remote_connection import (
 from gremlin_python.driver.protocol import GremlinServerWSProtocol
 from gremlin_python.driver.serializer import (
     GraphSONMessageSerializer, GraphSONSerializersV2d0, GraphSONSerializersV3d0,
-    GraphBinaryMessageSerializerV1)
+    GraphBinarySerializersV1)
 from gremlin_python.driver.tornado.transport import TornadoTransport
 
 gremlin_server_host = "localhost"
@@ -88,7 +88,7 @@ def remote_connection(request):
     try:
         if request.param == 'graphbinaryv1':
             remote_conn = DriverRemoteConnection(gremlin_server_url, 'gmodern',
-                                                 message_serializer=serializer.GraphBinaryMessageSerializerV1())
+                                                 message_serializer=serializer.GraphBinarySerializersV1())
         elif request.param == 'graphsonv2':
             remote_conn = DriverRemoteConnection(gremlin_server_url, 'gmodern',
                                                  message_serializer=serializer.GraphSONSerializersV2d0())
@@ -129,4 +129,4 @@ def graphson_serializer_v3(request):
 
 @pytest.fixture
 def graphbinary_serializer_v1(request):
-    return GraphBinaryMessageSerializerV1()
+    return GraphBinarySerializersV1()
diff --git a/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py b/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py
index 2ecd113..b2b4a7d 100644
--- a/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py
+++ b/gremlin-python/src/main/jython/tests/driver/test_driver_remote_connection.py
@@ -16,7 +16,6 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-import pytest
 
 from tornado import ioloop, gen
 
@@ -25,7 +24,6 @@ from gremlin_python.statics import long
 from gremlin_python.driver.driver_remote_connection import (
     DriverRemoteConnection)
 from gremlin_python.process.traversal import Traverser
-from gremlin_python.process.traversal import TraversalStrategy
 from gremlin_python.process.traversal import P
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.process.anonymous_traversal import traversal
@@ -38,7 +36,6 @@ __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 class TestDriverRemoteConnection(object):
     def test_traversals(self, remote_connection):
         statics.load_statics(globals())
-        assert "remoteconnection[ws://localhost:45940/gremlin,gmodern]" == str(remote_connection)
         g = traversal().withRemote(remote_connection)
 
         assert long(6) == g.V().count().toList()[0]
@@ -60,8 +57,7 @@ class TestDriverRemoteConnection(object):
         assert 4 == g.V()[2:].count().next()
         assert 2 == g.V()[:2].count().next()
         # #
-        results = g.withSideEffect('a', ['josh', 'peter']).V(1).out('created').in_('created').values('name').where(
-            within('a')).toList()
+        results = g.withSideEffect('a', ['josh', 'peter']).V(1).out('created').in_('created').values('name').where(P.within('a')).toList()
         assert 2 == len(results)
         assert 'josh' in results
         assert 'peter' in results
@@ -94,7 +90,6 @@ class TestDriverRemoteConnection(object):
 
     def test_iteration(self, remote_connection):
         statics.load_statics(globals())
-        assert "remoteconnection[ws://localhost:45940/gremlin,gmodern]" == str(remote_connection)
         g = traversal().withRemote(remote_connection)
 
         t = g.V().count()
@@ -134,16 +129,6 @@ class TestDriverRemoteConnection(object):
         statics.load_statics(globals())
         #
         g = traversal().withRemote(remote_connection). \
-            withStrategies(TraversalStrategy("SubgraphStrategy",
-                                             {"vertices": __.hasLabel("person"),
-                                              "edges": __.hasLabel("created")}))
-        assert 4 == g.V().count().next()
-        assert 0 == g.E().count().next()
-        assert 1 == g.V().label().dedup().count().next()
-        assert 4 == g.V().filter(lambda: ("lambda x: True", "gremlin-python")).count().next()
-        assert "person" == g.V().label().dedup().next()
-        #
-        g = traversal().withRemote(remote_connection). \
             withStrategies(SubgraphStrategy(vertices=__.hasLabel("person"), edges=__.hasLabel("created")))
         assert 4 == g.V().count().next()
         assert 0 == g.E().count().next()
@@ -168,118 +153,6 @@ class TestDriverRemoteConnection(object):
         assert 6 == g.V().count().next()
         assert 6 == g.E().count().next()
 
-    def test_side_effects(self, remote_connection):
-        statics.load_statics(globals())
-        #
-        g = traversal().withRemote(remote_connection)
-        ###
-        t = g.V().hasLabel("project").name.iterate()
-        assert 0 == len(t.side_effects.keys())
-        with pytest.raises(Exception):
-            m = t.side_effects["m"]
-        ###
-        t = g.V().out("created").groupCount("m").by("name")
-        results = t.toSet()
-        assert 2 == len(results)
-        assert Vertex(3) in results
-        assert Vertex(5) in results
-        assert 1 == len(t.side_effects.keys())
-        assert "m" in t.side_effects.keys()
-        m = t.side_effects["m"]
-        assert isinstance(m, dict)
-        assert 2 == len(m)
-        assert 3 == m["lop"]
-        assert 1 == m["ripple"]
-        assert isinstance(m["lop"], long)
-        assert isinstance(m["ripple"], long)
-
-        # check status attributes
-        assert "host" in t.side_effects.status_attributes
-
-        ##
-        t = g.V().out("created").groupCount("m").by("name").name.aggregate("n")
-        results = t.toSet()
-        assert 2 == len(results)
-        assert "lop" in results
-        assert "ripple" in results
-        assert 2 == len(t.side_effects.keys())
-        assert "m" in t.side_effects.keys()
-        assert "n" in t.side_effects.keys()
-        n = t.side_effects.get("n")
-        assert isinstance(n, dict)
-        assert 2 == len(n)
-        assert "lop" in n.keys()
-        assert "ripple" in n.keys()
-        assert 3 == n["lop"]
-        assert 1 == n["ripple"]
-
-        t = g.withSideEffect('m', 32).V().map(lambda: "x: x.sideEffects('m')")
-        results = t.toSet()
-        assert 1 == len(results)
-        assert 32 == list(results)[0]
-        assert 32 == t.side_effects['m']
-        assert 1 == len(t.side_effects.keys())
-        with pytest.raises(Exception):
-            x = t.side_effects["x"]
-
-        a = g.V().has("name", "marko").next()
-        b = g.V().has("name", "peter").next()
-        edge = g.withSideEffect("b", b).V(a).addE("knows").to("b").next()
-        assert "knows" == edge.label
-        assert a == edge.outV
-        assert b == edge.inV
-        g.V().has("name", "marko").outE("knows").where(__.inV().has("name", "peter")).drop().iterate()
-        ##
-        edge = g.withSideEffect("a", a).withSideEffect("b", b).V().limit(1).addE("knows").from_("a").to("b").next()
-        assert "knows" == edge.label
-        assert a == edge.outV
-        assert b == edge.inV
-        g.V().has("name", "marko").outE("knows").where(__.inV().has("name", "peter")).drop().iterate()
-
-    def test_side_effect_close(self, remote_connection):
-        g = traversal().withRemote(remote_connection)
-        t = g.V().aggregate('a').aggregate('b')
-        t.toList()
-
-        # The 'a' key should return some side effects
-        results = t.side_effects.get('a')
-        assert results
-
-        # Close result is None
-        results = t.side_effects.close()
-        assert not results
-
-        # Shouldn't get any new info from server
-        # 'b' isn't in local cache
-        results = t.side_effects.get('b')
-        assert not results
-
-        # But 'a' should still be cached locally
-        results = t.side_effects.get('a')
-        assert results
-
-        # 'a' should have been added to local keys cache, but not 'b'
-        results = t.side_effects.keys()
-        assert len(results) == 1
-        a, = results
-        assert a == 'a'
-
-        # Try to get 'b' directly from server, should throw error
-        with pytest.raises(Exception):
-            t.side_effects.value_lambda('b')
-
-    def test_promise(self, remote_connection):
-        g = traversal().withRemote(remote_connection)
-        future = g.V().aggregate('a').promise()
-        t = future.result()
-        assert len(t.toList()) == 6
-        a, = t.side_effects.keys()
-        assert a == 'a'
-        results = t.side_effects.get('a')
-        assert results
-        results = t.side_effects.close()
-        assert not results
-
 
 def test_in_tornado_app(remote_connection):
     # Make sure nothing weird with loops