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

xiazcy pushed a commit to branch python-http
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 2b1b0cb7b1e210f29a7f5e2e0c50b50fe3213120
Author: Yang Xia <[email protected]>
AuthorDate: Wed Jun 12 09:28:35 2024 -0700

    very basic connectivity to v4 server
---
 .../gremlin_python/driver/aiohttp/transport.py     |   13 +-
 .../main/python/gremlin_python/driver/client.py    |   20 +-
 .../main/python/gremlin_python/driver/protocol.py  |   29 +-
 .../main/python/gremlin_python/driver/request.py   |    5 +-
 .../main/python/gremlin_python/driver/resultset.py |    7 +-
 .../python/gremlin_python/driver/serializer.py     |  101 +-
 .../gremlin_python/structure/io/graphbinaryV4.py   | 1178 ++++++++++++++++++++
 gremlin-python/src/main/python/tests/conftest.py   |    7 +-
 .../main/python/tests/driver/test_serializer.py    |    8 +
 gremlin-server/conf/gremlin-server-modern.yaml     |   11 +-
 10 files changed, 1332 insertions(+), 47 deletions(-)

diff --git 
a/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py 
b/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
index 462247a137..c43ded6f58 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/aiohttp/transport.py
@@ -195,8 +195,8 @@ class AiohttpHTTPTransport(AbstractBaseTransport):
         async def async_write():
             basic_auth = None
             # basic password authentication for https connections
-            if message['auth']:
-                basic_auth = aiohttp.BasicAuth(message['auth']['username'], 
message['auth']['password'])
+            # if message['auth']:
+            #     basic_auth = aiohttp.BasicAuth(message['auth']['username'], 
message['auth']['password'])
             async with async_timeout.timeout(self._write_timeout):
                 self._http_req_resp = await 
self._client_session.post(url="/gremlin",
                                                                       
auth=basic_auth,
@@ -210,8 +210,15 @@ class AiohttpHTTPTransport(AbstractBaseTransport):
     def read(self):
         # Inner function to perform async read.
         async def async_read():
+            buffer = b""
+            # async for data, end_of_http_chunk in 
self._http_req_resp.content.iter_chunks():
+            #     buffer += data
+            #     if end_of_http_chunk:
+            #         print('ended')
+            #     print(buffer)
             async with async_timeout.timeout(self._read_timeout):
-                return {"content": await self._http_req_resp.read(),
+                data = await self._http_req_resp.read()
+                return {"content": data,
                         "ok": self._http_req_resp.ok,
                         "status": self._http_req_resp.status}
 
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/client.py 
b/gremlin-python/src/main/python/gremlin_python/driver/client.py
index 74d90a32d0..624f55b973 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/client.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/client.py
@@ -180,23 +180,19 @@ class Client:
             raise Exception("Client is closed")
 
         log.debug("message '%s'", str(message))
-        args = {'gremlin': message, 'aliases': {'g': self._traversal_source}}
-        processor = ''
-        op = 'eval'
+        # args = {'gremlin': message, 'fields': {'g': self._traversal_source}}
+        fields = {'g': self._traversal_source}
         if isinstance(message, traversal.Bytecode):
-            op = 'bytecode'
-            processor = 'traversal'
+            fields['gremlinType'] = 'bytecode'
+        elif isinstance(message, str):
+            fields['gremlinType'] = 'eval'
 
         if isinstance(message, str) and bindings:
-            args['bindings'] = bindings
-
-        if self._session_enabled:
-            args['session'] = str(self._session)
-            processor = 'session'
+            fields['bindings'] = bindings
 
         if isinstance(message, traversal.Bytecode) or isinstance(message, str):
-            log.debug("processor='%s', op='%s', args='%s'", str(processor), 
str(op), str(args))
-            message = request.RequestMessage(processor=processor, op=op, 
args=args)
+            log.debug("fields='%s', gremlin='%s'", str(fields), str(message))
+            message = request.RequestMessageV4(fields=fields, gremlin=message)
 
         conn = self._pool.get(True)
         if request_options:
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/protocol.py 
b/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
index 6afd7c295d..b0bbfba9d3 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/protocol.py
@@ -61,7 +61,7 @@ class AbstractBaseProtocol(metaclass=abc.ABCMeta):
         pass
 
     @abc.abstractmethod
-    def write(self, request_id, request_message):
+    def write(self, request_message):
         pass
 
 
@@ -206,7 +206,7 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
     def connection_made(self, transport):
         super(GremlinServerHTTPProtocol, self).connection_made(transport)
 
-    def write(self, request_id, request_message):
+    def write(self, request_message):
 
         basic_auth = {}
         if self._username and self._password:
@@ -217,13 +217,13 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
         message = {
             'headers': {'CONTENT-TYPE': content_type,
                         'ACCEPT': content_type},
-            'payload': self._message_serializer.serialize_message(request_id, 
request_message),
+            'payload': 
self._message_serializer.serialize_message(request_message),
             'auth': basic_auth
         }
 
         self._transport.write(message)
 
-    def data_received(self, response, results_dict):
+    def data_received(self, response, result_set):
         # if Gremlin Server cuts off then we get a None for the message
         if response is None:
             log.error("Received empty message from server.")
@@ -232,8 +232,6 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
 
         if response['ok']:
             message = 
self._message_serializer.deserialize_message(response['content'])
-            request_id = message['requestId']
-            result_set = results_dict[request_id] if request_id in 
results_dict else ResultSet(None, None)
             status_code = message['status']['code']
             aggregate_to = message['result']['meta'].get('aggregateTo', 'list')
             data = message['result']['data']
@@ -241,19 +239,20 @@ class GremlinServerHTTPProtocol(AbstractBaseProtocol):
 
             if status_code == 204:
                 result_set.stream.put_nowait([])
-                del results_dict[request_id]
                 return status_code
             elif status_code in [200, 206]:
                 result_set.stream.put_nowait(data)
-                if status_code == 200:
-                    result_set.status_attributes = 
message['status']['attributes']
-                    del results_dict[request_id]
+                # if status_code == 200:
+                #     result_set.status_attributes = 
message['status']['attributes']
                 return status_code
+            else:
+                log.error("\r\nReceived error message '%s'\r\n\r\nWith result 
set '%s'",
+                          str(message), str(result_set))
+                raise GremlinServerError({'code': status_code,
+                                          'message': 
message['status']['message'],
+                                          'attributes': {}})
         else:
-            # This message is going to be huge and kind of hard to read, but 
in the event of an error,
-            # it can provide invaluable info, so space it out appropriately.
-            log.error("\r\nReceived error message '%s'\r\n\r\nWith results 
dictionary '%s'",
-                      str(response['content']), str(results_dict))
+            log.error("\r\nReceived error message '%s'\r\n\r\nWith result set 
'%s'",
+                      str(response['content']), str(result_set))
             body = json.loads(response['content'])
-            del results_dict[body['requestId']]
             raise GremlinServerError({'code': response['status'], 'message': 
body['message'], 'attributes': {}})
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/request.py 
b/gremlin-python/src/main/python/gremlin_python/driver/request.py
index 73ee727df0..830b31f34a 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/request.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/request.py
@@ -20,6 +20,5 @@ import collections
 
 __author__ = 'David M. Brown ([email protected])'
 
-
-RequestMessage = collections.namedtuple(
-    'RequestMessage', ['processor', 'op', 'args'])
+RequestMessageV4 = collections.namedtuple(
+    'RequestMessageV4', ['fields', 'gremlin'])
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/resultset.py 
b/gremlin-python/src/main/python/gremlin_python/driver/resultset.py
index 66566fe8c4..8268fa7d40 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/resultset.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/resultset.py
@@ -23,9 +23,8 @@ __author__ = 'David M. Brown ([email protected])'
 
 class ResultSet:
 
-    def __init__(self, stream, request_id):
+    def __init__(self, stream):
         self._stream = stream
-        self._request_id = request_id
         self._done = None
         self._aggregate_to = None
         self._status_attributes = {}
@@ -46,10 +45,6 @@ class ResultSet:
     def status_attributes(self, val):
         self._status_attributes = val
 
-    @property
-    def request_id(self):
-        return self._request_id
-
     @property
     def stream(self):
         return self._stream
diff --git a/gremlin-python/src/main/python/gremlin_python/driver/serializer.py 
b/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
index 6b522e50b3..65423b5ecb 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
@@ -30,6 +30,7 @@ except ImportError:
     import json
 
 from gremlin_python.structure.io import graphbinaryV1
+from gremlin_python.structure.io import graphbinaryV4
 from gremlin_python.structure.io import graphsonV2d0
 from gremlin_python.structure.io import graphsonV3d0
 
@@ -72,10 +73,10 @@ class Session(Processor):
     def bytecode(self, args):
         gremlin = args['gremlin']
         args['gremlin'] = self._writer.to_dict(gremlin)
-        aliases = args.get('aliases', '')
-        if not aliases:
-            aliases = {'g': 'g'}
-        args['aliases'] = aliases
+        bindings = args.get('bindings', '')
+        if not bindings:
+            bindings = {'g': 'g'}
+        args['bindings'] = bindings
         return args
 
 
@@ -292,3 +293,95 @@ class GraphBinarySerializersV1(object):
                           'data': result}}
 
         return msg
+
+class GraphBinarySerializersV4(object):
+    DEFAULT_READER_CLASS = graphbinaryV4.GraphBinaryReader
+    DEFAULT_WRITER_CLASS = graphbinaryV4.GraphBinaryWriter
+    DEFAULT_VERSION = b"application/vnd.graphbinary-v4.0"
+
+    max_int64 = 0xFFFFFFFFFFFFFFFF
+    header_struct = struct.Struct('>b32sBQQ')
+    header_pack = header_struct.pack
+    int_pack = graphbinaryV4.int32_pack
+    int32_unpack = struct.Struct(">i").unpack
+
+    def __init__(self, reader=None, writer=None, version=None):
+        if not version:
+            version = self.DEFAULT_VERSION
+        self._version = version
+        if not reader:
+            reader = self.DEFAULT_READER_CLASS()
+        self._graphbinary_reader = reader
+        if not writer:
+            writer = self.DEFAULT_WRITER_CLASS()
+        self._graphbinary_writer = writer
+        self.standard = Standard(writer)
+        self.traversal = Traversal(writer)
+        self.session = Session(writer)
+
+    @property
+    def version(self):
+        """Read only property"""
+        return self._version
+
+    def get_processor(self, processor):
+        processor = getattr(self, processor, None)
+        if not processor:
+            raise Exception("Unknown processor")
+        return processor
+
+    def serialize_message(self, request_message):
+        message = self.build_message(request_message.fields, 
request_message.gremlin)
+        return message
+
+    def build_message(self, fields, gremlin):
+        message = {
+            'fields': fields,
+            'gremlin': gremlin
+        }
+        return self.finalize_message(message, 0x20, self.version)
+
+    def finalize_message(self, message, mime_len, mime_type):
+        ba = bytearray()
+
+        ba.extend(graphbinaryV4.uint8_pack(0x81))
+        print(bytes(ba))
+        fields = message["fields"]
+        ba.extend(self.int_pack(len(fields)))
+        for k, v in fields.items():
+            self._graphbinary_writer.to_dict(k, ba)
+            self._graphbinary_writer.to_dict(v, ba)
+
+        gremlin = message['gremlin']
+        if isinstance(gremlin, bytearray):
+            ba.extend(gremlin)
+        else:
+            self._graphbinary_writer.to_dict(gremlin, ba)
+
+        print(bytes(ba))
+
+        return bytes(ba)
+
+    def deserialize_message(self, message):
+        # for parsing string message via HTTP connections
+        b = io.BytesIO(base64.b64decode(message) if isinstance(message, str) 
else message)
+
+        b.read(1)  # version
+
+        result = self._graphbinary_reader.to_object(b)  # data
+        # marker = self._graphbinary_reader.to_object(b, 
graphbinaryV4.DataType.marker, nullable=True)
+        marker = b.read(3)  # need to create marker class & serializer
+        status_code = self.int32_unpack(b.read(4))[0]  # status code
+        status_msg = self._graphbinary_reader.to_object(b, 
graphbinaryV4.DataType.string, nullable=True)
+        status_ex = self._graphbinary_reader.to_object(b, 
graphbinaryV4.DataType.string, nullable=True)
+        # meta_attrs = self._graphbinary_reader.to_object(b, 
graphbinaryV1.DataType.map, nullable=False)
+
+        b.close()
+
+        msg = {'status': {'code': status_code,
+                          'message': status_msg,
+                          'exception': status_ex},
+               'result': {'meta': {},
+                          'data': result}}
+
+        return msg
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
new file mode 100644
index 0000000000..d443fb0ec1
--- /dev/null
+++ 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV4.py
@@ -0,0 +1,1178 @@
+"""
+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.
+"""
+
+import datetime
+import calendar
+import uuid
+import math
+import io
+import struct
+from collections import OrderedDict
+import logging
+
+from struct import pack, unpack
+from aenum import Enum
+from datetime import timedelta
+from gremlin_python import statics
+from gremlin_python.statics import FloatType, BigDecimal, FunctionType, 
ShortType, IntType, LongType, BigIntType, \
+                                   TypeType, DictType, ListType, SetType, 
SingleByte, ByteBufferType, GremlinType, \
+                                   SingleChar
+from gremlin_python.process.traversal import Barrier, Binding, Bytecode, 
Cardinality, Column, Direction, DT, Merge, \
+                                             Operator, Order, Pick, Pop, P, 
Scope, TextP, Traversal, Traverser, \
+                                             TraversalStrategy, T
+from gremlin_python.process.graph_traversal import GraphTraversal
+from gremlin_python.structure.graph import Graph, Edge, Property, Vertex, 
VertexProperty, Path
+from gremlin_python.structure.io.util import HashableDict, SymbolUtil
+
+log = logging.getLogger(__name__)
+
+# When we fall back to a superclass's serializer, we iterate over this map.
+# We want that iteration order to be consistent, so we use an OrderedDict,
+# not a dict.
+_serializers = OrderedDict()
+_deserializers = {}
+
+
+class DataType(Enum):
+    null = 0xfe
+    int = 0x01
+    long = 0x02
+    string = 0x03
+    date = 0x04
+    timestamp = 0x05
+    clazz = 0x06
+    double = 0x07
+    float = 0x08
+    list = 0x09
+    map = 0x0a
+    set = 0x0b
+    uuid = 0x0c
+    edge = 0x0d
+    path = 0x0e
+    property = 0x0f
+    graph = 0x10                  # not supported - no graph object in python 
yet
+    vertex = 0x11
+    vertexproperty = 0x12
+    barrier = 0x13
+    binding = 0x14
+    bytecode = 0x15
+    cardinality = 0x16
+    column = 0x17
+    direction = 0x18
+    operator = 0x19
+    order = 0x1a
+    pick = 0x1b
+    pop = 0x1c
+    lambda_ = 0x1d
+    p = 0x1e
+    scope = 0x1f
+    t = 0x20
+    traverser = 0x21
+    bigdecimal = 0x22
+    biginteger = 0x23
+    byte = 0x24
+    bytebuffer = 0x25
+    short = 0x26
+    boolean = 0x27
+    textp = 0x28
+    traversalstrategy = 0x29
+    bulkset = 0x2a
+    tree = 0x2b                   # not supported - no tree object in Python 
yet
+    metrics = 0x2c
+    traversalmetrics = 0x2d
+    merge = 0x2e
+    dt = 0x2f
+    char = 0x80
+    duration = 0x81
+    marker = 0xfd
+    inetaddress = 0x82            # todo
+    instant = 0x83                # todo
+    localdate = 0x84              # todo
+    localdatetime = 0x85          # todo
+    localtime = 0x86              # todo
+    monthday = 0x87               # todo
+    offsetdatetime = 0x88         # todo
+    offsettime = 0x89             # todo
+    period = 0x8a                 # todo
+    year = 0x8b                   # todo
+    yearmonth = 0x8c              # todo
+    zonedatetime = 0x8d           # todo
+    zoneoffset = 0x8e             # todo
+    custom = 0x00                 # todo
+
+
+NULL_BYTES = [DataType.null.value, 0x01]
+
+
+def _make_packer(format_string):
+    packer = struct.Struct(format_string)
+    pack = packer.pack
+    unpack = lambda s: packer.unpack(s)[0]
+    return pack, unpack
+
+
+int64_pack, int64_unpack = _make_packer('>q')
+int32_pack, int32_unpack = _make_packer('>i')
+int16_pack, int16_unpack = _make_packer('>h')
+int8_pack, int8_unpack = _make_packer('>b')
+uint64_pack, uint64_unpack = _make_packer('>Q')
+uint8_pack, uint8_unpack = _make_packer('>B')
+float_pack, float_unpack = _make_packer('>f')
+double_pack, double_unpack = _make_packer('>d')
+
+
+class GraphBinaryTypeType(type):
+    def __new__(mcs, name, bases, dct):
+        cls = super(GraphBinaryTypeType, mcs).__new__(mcs, name, bases, dct)
+        if not name.startswith('_'):
+            if cls.python_type:
+                _serializers[cls.python_type] = cls
+            if cls.graphbinary_type:
+                _deserializers[cls.graphbinary_type] = cls
+        return cls
+
+
+class GraphBinaryWriter(object):
+    def __init__(self, serializer_map=None):
+        self.serializers = _serializers.copy()
+        if serializer_map:
+            self.serializers.update(serializer_map)
+
+    def write_object(self, object_data):
+        return self.to_dict(object_data)
+
+    def to_dict(self, obj, to_extend=None):
+        if to_extend is None:
+            to_extend = bytearray()
+
+        if obj is None:
+            to_extend.extend(NULL_BYTES)
+            return
+
+        try:
+            t = type(obj)
+            return self.serializers[t].dictify(obj, self, to_extend)
+        except KeyError:
+            for key, serializer in self.serializers.items():
+                if isinstance(obj, key):
+                    return serializer.dictify(obj, self, to_extend)
+
+        if isinstance(obj, dict):
+            return dict((self.to_dict(k, to_extend), self.to_dict(v, 
to_extend)) for k, v in obj.items())
+        elif isinstance(obj, set):
+            return set([self.to_dict(o, to_extend) for o in obj])
+        elif isinstance(obj, list):
+            return [self.to_dict(o, to_extend) for o in obj]
+        else:
+            return obj
+
+
+class GraphBinaryReader(object):
+    def __init__(self, deserializer_map=None):
+        self.deserializers = _deserializers.copy()
+        if deserializer_map:
+            self.deserializers.update(deserializer_map)
+
+    def read_object(self, b):
+        if isinstance(b, bytearray):
+            return self.to_object(io.BytesIO(b))
+        elif isinstance(b, io.BufferedIOBase):
+            return self.to_object(b)
+
+    def to_object(self, buff, data_type=None, nullable=True):
+        if data_type is None:
+            bt = uint8_unpack(buff.read(1))
+            if bt == DataType.null.value:
+                if nullable:
+                    buff.read(1)
+                return None
+            return self.deserializers[DataType(bt)].objectify(buff, self, 
nullable)
+        else:
+            return self.deserializers[data_type].objectify(buff, self, 
nullable)
+
+
+class _GraphBinaryTypeIO(object, metaclass=GraphBinaryTypeType):
+    python_type = None
+    graphbinary_type = None
+
+    @classmethod
+    def prefix_bytes(cls, graphbin_type, as_value=False, nullable=True, 
to_extend=None):
+        if to_extend is None:
+            to_extend = bytearray()
+
+        if not as_value:
+            to_extend += uint8_pack(graphbin_type.value)
+
+        if nullable:
+            to_extend += int8_pack(0)
+
+        return to_extend
+
+    @classmethod
+    def read_int(cls, buff):
+        return int32_unpack(buff.read(4))
+
+    @classmethod
+    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, to_extend, as_value=False, nullable=True):
+        raise NotImplementedError()
+
+    def objectify(self, d, reader, nullable=True):
+        raise NotImplementedError()
+        
+
+class LongIO(_GraphBinaryTypeIO):
+
+    python_type = LongType
+    graphbinary_type = DataType.long
+    byte_format_pack = int64_pack
+    byte_format_unpack = int64_unpack
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        if obj < -9223372036854775808 or obj > 9223372036854775807:
+            raise Exception("Value too big, please use bigint Gremlin type")
+        else:
+            cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, 
to_extend)
+            to_extend.extend(cls.byte_format_pack(obj))
+            return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: 
int64_unpack(buff.read(8)), nullable)
+
+
+class IntIO(LongIO):
+
+    python_type = IntType
+    graphbinary_type = DataType.int
+    byte_format_pack = int32_pack
+    byte_format_unpack = int32_unpack
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: cls.read_int(b), 
nullable)
+
+
+class ShortIO(LongIO):
+
+    python_type = ShortType
+    graphbinary_type = DataType.short
+    byte_format_pack = int16_pack
+    byte_format_unpack = int16_unpack
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: 
int16_unpack(buff.read(2)), nullable)
+
+
+class BigIntIO(_GraphBinaryTypeIO):
+
+    python_type = BigIntType
+    graphbinary_type = DataType.biginteger
+
+    @classmethod
+    def write_bigint(cls, obj, to_extend):
+        length = (obj.bit_length() + 7) // 8
+        if obj > 0:
+            b = obj.to_bytes(length, byteorder='big')
+            to_extend.extend(int32_pack(length + 1))
+            to_extend.extend(int8_pack(0))
+            to_extend.extend(b)
+        else:
+            # handle negative
+            b = obj.to_bytes(length, byteorder='big', signed=True)
+            to_extend.extend(int32_pack(length))
+            to_extend.extend(b)
+        return to_extend
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        return cls.write_bigint(obj, to_extend)
+
+    @classmethod
+    def read_bigint(cls, buff):
+        size = cls.read_int(buff)
+        return int.from_bytes(buff.read(size), byteorder='big', signed=True)
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=False):
+        return cls.is_null(buff, reader, lambda b, r: cls.read_bigint(b), 
nullable)
+
+
+class DateIO(_GraphBinaryTypeIO):
+
+    python_type = datetime.datetime
+    graphbinary_type = DataType.date
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        try:
+            timestamp_seconds = calendar.timegm(obj.utctimetuple())
+            pts = timestamp_seconds * 1e3 + getattr(obj, 'microsecond', 0) / 
1e3
+        except AttributeError:
+            pts = calendar.timegm(obj.timetuple()) * 1e3
+
+        ts = int(round(pts))
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int64_pack(ts))
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader,
+                           lambda b, r: 
datetime.datetime.utcfromtimestamp(int64_unpack(b.read(8)) / 1000.0),
+                           nullable)
+
+
+# Based on current implementation, this class must always be declared before 
FloatIO.
+# Seems pretty fragile for future maintainers. Maybe look into this.
+class TimestampIO(_GraphBinaryTypeIO):
+    python_type = statics.timestamp
+    graphbinary_type = DataType.timestamp
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        # Java timestamp expects milliseconds integer - Have to use int 
because of legacy Python
+        ts = int(round(obj * 1000))
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int64_pack(ts))
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        # Python timestamp expects seconds
+        return cls.is_null(buff, reader, lambda b, r: 
statics.timestamp(int64_unpack(b.read(8)) / 1000.0),
+                           nullable)
+    
+
+def _long_bits_to_double(bits):
+    return unpack('d', pack('Q', bits))[0]
+
+
+NAN = _long_bits_to_double(0x7ff8000000000000)
+POSITIVE_INFINITY = _long_bits_to_double(0x7ff0000000000000)
+NEGATIVE_INFINITY = _long_bits_to_double(0xFff0000000000000)
+
+
+class FloatIO(LongIO):
+
+    python_type = FloatType
+    graphbinary_type = DataType.float
+    graphbinary_base_type = DataType.float
+    byte_format_pack = float_pack
+    byte_format_unpack = float_unpack
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        if math.isnan(obj):
+            cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, 
to_extend)
+            to_extend.extend(cls.byte_format_pack(NAN))
+        elif math.isinf(obj) and obj > 0:
+            cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, 
to_extend)
+            to_extend.extend(cls.byte_format_pack(POSITIVE_INFINITY))
+        elif math.isinf(obj) and obj < 0:
+            cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, 
to_extend)
+            to_extend.extend(cls.byte_format_pack(NEGATIVE_INFINITY))
+        else:
+            cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, 
to_extend)
+            to_extend.extend(cls.byte_format_pack(obj))
+
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: float_unpack(b.read(4)), 
nullable)
+
+
+class DoubleIO(FloatIO):
+    """
+    Floats basically just fall through to double serialization.
+    """
+
+    graphbinary_type = DataType.double
+    graphbinary_base_type = DataType.double
+    byte_format_pack = double_pack
+    byte_format_unpack = double_unpack
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: 
double_unpack(b.read(8)), nullable)
+
+
+class BigDecimalIO(_GraphBinaryTypeIO):
+
+    python_type = BigDecimal
+    graphbinary_type = DataType.bigdecimal
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int32_pack(obj.scale))
+        return BigIntIO.write_bigint(obj.unscaled_value, to_extend)
+
+    @classmethod
+    def _read(cls, buff):
+        scale = int32_unpack(buff.read(4))
+        unscaled_value = BigIntIO.read_bigint(buff)
+        return BigDecimal(scale, unscaled_value)
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=False):
+        return cls.is_null(buff, reader, lambda b, r: cls._read(b), nullable)
+
+
+class CharIO(_GraphBinaryTypeIO):
+    python_type = SingleChar
+    graphbinary_type = DataType.char
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(obj.encode("utf-8"))
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_char, nullable)
+
+    @classmethod
+    def _read_char(cls, b, r):
+        max_bytes = 4
+        x = b.read(1)
+        while max_bytes > 0:
+            max_bytes = max_bytes - 1
+            try:
+                return x.decode("utf-8")
+            except UnicodeDecodeError:
+                x += b.read(1)
+
+
+class StringIO(_GraphBinaryTypeIO):
+
+    python_type = str
+    graphbinary_type = DataType.string
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        str_bytes = obj.encode("utf-8")
+        to_extend += int32_pack(len(str_bytes))
+        to_extend += str_bytes
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: 
b.read(cls.read_int(b)).decode("utf-8"), nullable)
+
+
+class ListIO(_GraphBinaryTypeIO):
+
+    python_type = list
+    graphbinary_type = DataType.list
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int32_pack(len(obj)))
+        for item in obj:
+            writer.to_dict(item, to_extend)
+
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_list, nullable)
+
+    @classmethod
+    def _read_list(cls, b, r):
+        size = cls.read_int(b)
+        the_list = []
+        while size > 0:
+            the_list.append(r.read_object(b))
+            size = size - 1
+
+        return the_list
+
+
+class SetDeserializer(ListIO):
+
+    python_type = SetType
+    graphbinary_type = DataType.set
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return set(ListIO.objectify(buff, reader, nullable))
+
+
+class MapIO(_GraphBinaryTypeIO):
+
+    python_type = DictType
+    graphbinary_type = DataType.map
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        to_extend.extend(int32_pack(len(obj)))
+        for k, v in obj.items():
+            writer.to_dict(k, to_extend)
+            writer.to_dict(v, to_extend)
+
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_map, nullable)
+
+    @classmethod
+    def _read_map(cls, b, r):
+        size = cls.read_int(b)
+        the_dict = {}
+        while size > 0:
+            k = HashableDict.of(r.read_object(b))
+            v = r.read_object(b)
+            the_dict[k] = v
+            size = size - 1
+
+        return the_dict
+
+
+class UuidIO(_GraphBinaryTypeIO):
+
+    python_type = uuid.UUID
+    graphbinary_type = DataType.uuid
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(obj.bytes)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: 
uuid.UUID(bytes=b.read(16)), nullable)
+
+
+class EdgeIO(_GraphBinaryTypeIO):
+
+    python_type = Edge
+    graphbinary_type = DataType.edge
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        writer.to_dict(obj.id, to_extend)
+        StringIO.dictify(obj.label, writer, to_extend, True, False)
+        writer.to_dict(obj.inV.id, to_extend)
+        StringIO.dictify(obj.inV.label, writer, to_extend, True, False)
+        writer.to_dict(obj.outV.id, to_extend)
+        StringIO.dictify(obj.outV.label, writer, to_extend, True, False)
+        to_extend.extend(NULL_BYTES)
+        to_extend.extend(NULL_BYTES)
+
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_edge, nullable)
+
+    @classmethod
+    def _read_edge(cls, b, r):
+        edgeid = r.read_object(b)
+        edgelbl = r.to_object(b, DataType.string, False)
+        inv = Vertex(r.read_object(b), r.to_object(b, DataType.string, False))
+        outv = Vertex(r.read_object(b), r.to_object(b, DataType.string, False))
+        b.read(2)
+        properties = r.read_object(b)
+        edge = Edge(edgeid, outv, edgelbl, inv, properties)
+        return edge
+
+
+class PathIO(_GraphBinaryTypeIO):
+
+    python_type = Path
+    graphbinary_type = DataType.path
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        writer.to_dict(obj.labels, to_extend)
+        writer.to_dict(obj.objects, to_extend)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: Path(r.read_object(b), 
r.read_object(b)), nullable)
+
+
+class PropertyIO(_GraphBinaryTypeIO):
+
+    python_type = Property
+    graphbinary_type = DataType.property
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        StringIO.dictify(obj.key, writer, to_extend, True, False)
+        writer.to_dict(obj.value, to_extend)
+        to_extend.extend(NULL_BYTES)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_property, nullable)
+
+    @classmethod
+    def _read_property(cls, b, r):
+        p = Property(r.to_object(b, DataType.string, False), r.read_object(b), 
None)
+        b.read(2)
+        return p
+
+
+class TinkerGraphIO(_GraphBinaryTypeIO):
+
+    python_type = Graph
+    graphbinary_type = DataType.graph
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        raise AttributeError("TinkerGraph serialization is not currently 
supported by gremlin-python")
+
+    @classmethod
+    def objectify(cls, b, reader, as_value=False):
+        raise AttributeError("TinkerGraph deserialization is not currently 
supported by gremlin-python")
+
+
+class VertexIO(_GraphBinaryTypeIO):
+
+    python_type = Vertex
+    graphbinary_type = DataType.vertex
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        writer.to_dict(obj.id, to_extend)
+        StringIO.dictify(obj.label, writer, to_extend, True, False)
+        to_extend.extend(NULL_BYTES)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_vertex, nullable)
+
+    @classmethod
+    def _read_vertex(cls, b, r):
+        vertex = Vertex(r.read_object(b), r.to_object(b, DataType.string, 
False), r.read_object(b))
+        return vertex
+
+
+class VertexPropertyIO(_GraphBinaryTypeIO):
+
+    python_type = VertexProperty
+    graphbinary_type = DataType.vertexproperty
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        writer.to_dict(obj.id, to_extend)
+        StringIO.dictify(obj.label, writer, to_extend, True, False)
+        writer.to_dict(obj.value, to_extend)
+        to_extend.extend(NULL_BYTES)
+        to_extend.extend(NULL_BYTES)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_vertexproperty, nullable)
+
+    @classmethod
+    def _read_vertexproperty(cls, b, r):
+        vp = VertexProperty(r.read_object(b), r.to_object(b, DataType.string, 
False), r.read_object(b), None)
+        b.read(2)
+        vp.properties = r.read_object(b)
+        return vp
+
+
+class _EnumIO(_GraphBinaryTypeIO):
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        StringIO.dictify(SymbolUtil.to_camel_case(str(obj.name)), writer, 
to_extend)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_enumval, nullable)
+
+    @classmethod
+    def _read_enumval(cls, b, r):
+        enum_name = r.to_object(b)
+        return cls.python_type[SymbolUtil.to_snake_case(enum_name)]
+
+
+class BarrierIO(_EnumIO):
+    graphbinary_type = DataType.barrier
+    python_type = Barrier
+
+
+class CardinalityIO(_EnumIO):
+    graphbinary_type = DataType.cardinality
+    python_type = Cardinality
+
+
+class ColumnIO(_EnumIO):
+    graphbinary_type = DataType.column
+    python_type = Column
+
+
+class DirectionIO(_EnumIO):
+    graphbinary_type = DataType.direction
+    python_type = Direction
+
+    @classmethod
+    def _read_enumval(cls, b, r):
+        # Direction needs to retain all CAPS. note that to_/from_ are really 
just aliases of IN/OUT
+        # so they don't need to be accounted for in serialization
+        enum_name = r.to_object(b)
+        return cls.python_type[enum_name]
+
+
+class OperatorIO(_EnumIO):
+    graphbinary_type = DataType.operator
+    python_type = Operator
+
+
+class OrderIO(_EnumIO):
+    graphbinary_type = DataType.order
+    python_type = Order
+
+
+class PickIO(_EnumIO):
+    graphbinary_type = DataType.pick
+    python_type = Pick
+
+
+class PopIO(_EnumIO):
+    graphbinary_type = DataType.pop
+    python_type = Pop
+
+
+class BindingIO(_GraphBinaryTypeIO):
+
+    python_type = Binding
+    graphbinary_type = DataType.binding
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        StringIO.dictify(obj.key, writer, to_extend, True, False)
+        writer.to_dict(obj.value, to_extend)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, lambda b, r: Binding(r.to_object(b, 
DataType.string, False),
+                                                              
reader.read_object(b)), nullable)
+
+
+class BytecodeIO(_GraphBinaryTypeIO):
+    python_type = Bytecode
+    graphbinary_type = DataType.bytecode
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        # cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        bc = obj.bytecode if isinstance(obj, Traversal) else obj
+        to_extend.extend(int32_pack(len(bc.step_instructions)))
+        for inst in bc.step_instructions:
+            inst_name, inst_args = inst[0], inst[1:] if len(inst) > 1 else []
+            StringIO.dictify(inst_name, writer, to_extend, True, False)
+            to_extend.extend(int32_pack(len(inst_args)))
+            for arg in inst_args:
+                writer.to_dict(arg, to_extend)
+
+        to_extend.extend(int32_pack(len(bc.source_instructions)))
+        for inst in bc.source_instructions:
+            inst_name, inst_args = inst[0], inst[1:] if len(inst) > 1 else []
+            StringIO.dictify(inst_name, writer, to_extend, True, False)
+            to_extend.extend(int32_pack(len(inst_args)))
+            for arg in inst_args:
+                if isinstance(arg, TypeType):
+                    writer.to_dict(GremlinType(arg().fqcn), to_extend)
+                else:
+                    writer.to_dict(arg, to_extend)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_bytecode, nullable)
+
+    @classmethod
+    def _read_bytecode(cls, b, r):
+        bytecode = Bytecode()
+
+        step_count = cls.read_int(b)
+        ix = 0
+        while ix < step_count:
+            inst = [r.to_object(b, DataType.string, False)]
+            inst_ct = cls.read_int(b)
+            iy = 0
+            while iy < inst_ct:
+                inst.append(r.read_object(b))
+                iy += 1
+            bytecode.step_instructions.append(inst)
+            ix += 1
+
+        source_count = cls.read_int(b)
+        ix = 0
+        while ix < source_count:
+            inst = [r.to_object(b, DataType.string, False)]
+            inst_ct = cls.read_int(b)
+            iy = 0
+            while iy < inst_ct:
+                inst.append(r.read_object(b))
+                iy += 1
+            bytecode.source_instructions.append(inst)
+            ix += 1
+
+        return bytecode
+
+
+class TraversalIO(BytecodeIO):
+    python_type = GraphTraversal
+
+
+class LambdaSerializer(_GraphBinaryTypeIO):
+
+    python_type = FunctionType
+    graphbinary_type = DataType.lambda_
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        lambda_result = obj()
+        script = lambda_result if isinstance(lambda_result, str) else 
lambda_result[0]
+        language = statics.default_lambda_language if 
isinstance(lambda_result, str) else lambda_result[1]
+
+        StringIO.dictify(language, writer, to_extend, True, False)
+
+        script_cleaned = script
+        script_args = -1
+
+        if language == "gremlin-groovy" and "->" in script:
+            # if the user has explicitly added parameters to the groovy 
closure then we can easily detect one or two
+            # arg lambdas - if we can't detect 1 or 2 then we just go with 
"unknown"
+            args = script[0:script.find("->")]
+            script_args = 2 if "," in args else 1
+
+        StringIO.dictify(script_cleaned, writer, to_extend, True, False)
+        to_extend.extend(int32_pack(script_args))
+
+        return to_extend
+
+
+class PSerializer(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.p
+    python_type = P
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        StringIO.dictify(obj.operator, writer, to_extend, True, False)
+
+        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)
+
+        to_extend.extend(int32_pack(len(args)))
+        for a in args:
+            writer.to_dict(a, to_extend)
+
+        return to_extend
+
+
+class DTIO(_EnumIO):
+    graphbinary_type = DataType.dt
+    python_type = DT
+
+
+class MergeIO(_EnumIO):
+    graphbinary_type = DataType.merge
+    python_type = Merge
+
+
+class ScopeIO(_EnumIO):
+    graphbinary_type = DataType.scope
+    python_type = Scope
+
+
+class TIO(_EnumIO):
+    graphbinary_type = DataType.t
+    python_type = T
+
+
+class TraverserIO(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.traverser
+    python_type = Traverser
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int64_pack(obj.bulk))
+        writer.to_dict(obj.object, to_extend)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_traverser, nullable)
+
+    @classmethod
+    def _read_traverser(cls, b, r):
+        bulk = int64_unpack(b.read(8))
+        obj = r.read_object(b)
+        return Traverser(obj, bulk=bulk)
+
+
+class ByteIO(_GraphBinaryTypeIO):
+    python_type = SingleByte
+    graphbinary_type = DataType.byte
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int8_pack(obj))
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader,
+                           lambda b, r: int.__new__(SingleByte, 
int8_unpack(b.read(1))),
+                           nullable)
+
+
+class ByteBufferIO(_GraphBinaryTypeIO):
+    python_type = ByteBufferType
+    graphbinary_type = DataType.bytebuffer
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int32_pack(len(obj)))
+        to_extend.extend(obj)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_bytebuffer, nullable)
+
+    @classmethod
+    def _read_bytebuffer(cls, b, r):
+        size = cls.read_int(b)
+        return ByteBufferType(b.read(size))
+
+
+class BooleanIO(_GraphBinaryTypeIO):
+    python_type = bool
+    graphbinary_type = DataType.boolean
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        to_extend.extend(int8_pack(0x01 if obj else 0x00))
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader,
+                           lambda b, r: True if int8_unpack(b.read(1)) == 0x01 
else False,
+                           nullable)
+
+
+class TextPSerializer(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.textp
+    python_type = TextP
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        StringIO.dictify(obj.operator, writer, to_extend, True, False)
+
+        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)
+
+        to_extend.extend(int32_pack(len(args)))
+        for a in args:
+            writer.to_dict(a, to_extend)
+
+        return to_extend
+
+
+class BulkSetDeserializer(_GraphBinaryTypeIO):
+
+    graphbinary_type = DataType.bulkset
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_bulkset, nullable)
+
+    @classmethod
+    def _read_bulkset(cls, b, r):
+        size = cls.read_int(b)
+        the_list = []
+        while size > 0:
+            itm = r.read_object(b)
+            bulk = int64_unpack(b.read(8))
+            for y in range(bulk):
+                the_list.append(itm)            
+            size = size - 1
+
+        return the_list
+
+
+class MetricsDeserializer(_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 = r.to_object(b, DataType.string, False)
+        name = r.to_object(b, DataType.string, False)
+        duration = r.to_object(b, DataType.long, nullable=False)
+        counts = r.to_object(b, DataType.map, nullable=False)
+        annotations = r.to_object(b, DataType.map, nullable=False)
+        metrics = r.to_object(b, DataType.list, nullable=False)
+
+        return {"id": metricid,
+                "name": name,
+                "dur": duration,
+                "counts": counts,
+                "annotations": annotations,
+                "metrics": metrics}
+
+
+class TraversalMetricsDeserializer(_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.to_object(b, DataType.long, nullable=False)
+        metrics = r.to_object(b, DataType.list, nullable=False)
+
+        return {"dur": duration,
+                "metrics": metrics}
+
+
+class ClassSerializer(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.clazz
+    python_type = GremlinType
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        StringIO.dictify(obj.gremlin_type, writer, to_extend, True, False)
+        return to_extend
+
+
+class TraversalStrategySerializer(_GraphBinaryTypeIO):
+    graphbinary_type = DataType.traversalstrategy
+    python_type = TraversalStrategy
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+
+        ClassSerializer.dictify(GremlinType(obj.fqcn), writer, to_extend, 
True, False)
+        conf = {k: cls._convert(v) for k, v in obj.configuration.items()}
+        MapIO.dictify(conf, writer, to_extend, True, False)
+
+        return to_extend
+
+    @classmethod
+    def _convert(cls, v):
+        return v.bytecode if isinstance(v, Traversal) else v
+
+
+class DurationIO(_GraphBinaryTypeIO):
+    python_type = timedelta
+    graphbinary_type = DataType.duration
+
+    @classmethod
+    def dictify(cls, obj, writer, to_extend, as_value=False, nullable=True):
+        cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+        LongIO.dictify(obj.seconds, writer, to_extend, True, False)
+        IntIO.dictify(obj.microseconds * 1000, writer, to_extend, True, False)
+        return to_extend
+
+    @classmethod
+    def objectify(cls, buff, reader, nullable=True):
+        return cls.is_null(buff, reader, cls._read_duration, nullable)
+    
+    @classmethod
+    def _read_duration(cls, b, r):
+        seconds = r.to_object(b, DataType.long, False)
+        nanos = r.to_object(b, DataType.int, False)
+        return timedelta(seconds=seconds, microseconds=nanos / 1000)
+
+
+# class MarkerIO(_GraphBinaryTypeIO):
+#     python_type = SingleByte
+#     graphbinary_type = DataType.marker
+#
+#     @classmethod
+#     def dictify(cls, obj, writer, to_extend, as_value=True, nullable=True):
+#         cls.prefix_bytes(cls.graphbinary_type, as_value, nullable, to_extend)
+#         to_extend.extend(int8_pack(obj))
+#         return to_extend
+#
+#     @classmethod
+#     def objectify(cls, buff, reader, nullable=True):
+#         return cls.is_null(buff, reader,
+#                            lambda b, r: int.__new__(SingleByte, 
int8_unpack(b.read(1))),
+#                            nullable)
diff --git a/gremlin-python/src/main/python/tests/conftest.py 
b/gremlin-python/src/main/python/tests/conftest.py
index a48fb5fa27..284c859015 100644
--- a/gremlin-python/src/main/python/tests/conftest.py
+++ b/gremlin-python/src/main/python/tests/conftest.py
@@ -35,7 +35,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,
-    GraphBinarySerializersV1)
+    GraphBinarySerializersV1, GraphBinarySerializersV4)
 from gremlin_python.driver.aiohttp.transport import AiohttpTransport, 
AiohttpHTTPTransport
 
 gremlin_server_url = os.environ.get('GREMLIN_SERVER_URL', 
'ws://localhost:{}/gremlin')
@@ -281,6 +281,11 @@ def remote_connection_graphsonV2(request):
         return remote_conn
 
 
[email protected]
+def graphbinary_serializer_v4(request):
+    return GraphBinarySerializersV4()
+
+
 @pytest.fixture
 def graphson_serializer_v2(request):
     return GraphSONSerializersV2d0()
diff --git a/gremlin-python/src/main/python/tests/driver/test_serializer.py 
b/gremlin-python/src/main/python/tests/driver/test_serializer.py
index 843af97122..d259dc9c42 100644
--- a/gremlin-python/src/main/python/tests/driver/test_serializer.py
+++ b/gremlin-python/src/main/python/tests/driver/test_serializer.py
@@ -19,6 +19,7 @@
 from gremlin_python.structure.io import graphsonV2d0
 from gremlin_python.structure.io import graphsonV3d0
 from gremlin_python.structure.io import graphbinaryV1
+from gremlin_python.structure.io import graphbinaryV4
 
 
 __author__ = 'David M. Brown'
@@ -43,3 +44,10 @@ def 
test_graphbinary_serializer_v1(graphbinary_serializer_v1):
     assert isinstance(graphbinary_serializer_v1._graphbinary_reader, 
graphbinaryV1.GraphBinaryReader)
     assert isinstance(graphbinary_serializer_v1.standard._writer, 
graphbinaryV1.GraphBinaryWriter)
     assert isinstance(graphbinary_serializer_v1.traversal._writer, 
graphbinaryV1.GraphBinaryWriter)
+
+
+def test_graphbinary_serializer_v4(graphbinary_serializer_v4):
+    assert graphbinary_serializer_v4.version == 
b"application/vnd.graphbinary-v4.0"
+    assert isinstance(graphbinary_serializer_v4._graphbinary_reader, 
graphbinaryV4.GraphBinaryReader)
+    assert isinstance(graphbinary_serializer_v4.standard._writer, 
graphbinaryV4.GraphBinaryWriter)
+    assert isinstance(graphbinary_serializer_v4.traversal._writer, 
graphbinaryV4.GraphBinaryWriter)
diff --git a/gremlin-server/conf/gremlin-server-modern.yaml 
b/gremlin-server/conf/gremlin-server-modern.yaml
index 1a477e1f4a..db7f261f64 100644
--- a/gremlin-server/conf/gremlin-server-modern.yaml
+++ b/gremlin-server/conf/gremlin-server-modern.yaml
@@ -18,6 +18,7 @@
 host: localhost
 port: 8182
 evaluationTimeout: 30000
+channelizer: org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer
 graphs: {
   graph: conf/tinkergraph-empty.properties}
 scriptEngines: {
@@ -28,9 +29,9 @@ scriptEngines: {
                org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: 
{classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
                org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: 
{files: [scripts/generate-modern.groovy]}}}}
 serializers:
-  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }}      
      # application/json
-  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 }          
                                                                                
                 # application/vnd.graphbinary-v1.0
-  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { 
serializeResultToString: true }}                                                
                 # application/vnd.graphbinary-v1.0-stringd
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV4, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }}      
      # application/json
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4 }          
                                                                                
                 # application/vnd.graphbinary-v1.0
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4, config: { 
serializeResultToString: true }}                                                
                 # application/vnd.graphbinary-v1.0-stringd
 metrics: {
   slf4jReporter: {enabled: true, interval: 180000}}
 strictTransactionManagement: false
@@ -42,3 +43,7 @@ maxChunkSize: 8192
 maxContentLength: 10485760
 maxAccumulationBufferComponents: 1024
 resultIterationBatchSize: 64
+writeBufferLowWaterMark: 32768
+writeBufferHighWaterMark: 65536
+ssl: {
+  enabled: false}
\ No newline at end of file

Reply via email to