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

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

commit 697c2065516bcd9ebca406a0a18b3113f28bd92c
Author: Yang Xia <[email protected]>
AuthorDate: Tue Oct 22 14:18:23 2024 -0700

    enable basic GraphSONV4 connectivity in python
---
 .../python/gremlin_python/driver/serializer.py     |  54 +-
 .../gremlin_python/structure/io/graphsonV4.py      | 730 +++++++++++++++++++++
 .../tests/driver/test_driver_remote_connection.py  |  22 +
 3 files changed, 805 insertions(+), 1 deletion(-)

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 94099f4c5d..9aa4a7c0cc 100644
--- a/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
+++ b/gremlin-python/src/main/python/gremlin_python/driver/serializer.py
@@ -31,11 +31,63 @@ try:
 except ImportError:
     import json
 
-from gremlin_python.structure.io import graphbinaryV4
+from gremlin_python.structure.io import graphbinaryV4, graphsonV4
 from gremlin_python.structure.io.util import Marker
 
 __author__ = 'David M. Brown ([email protected]), Lyndon Bauto 
([email protected])'
 
+"""
+GraphSONV4
+"""
+
+class GraphSONSerializerV4(object):
+    """
+    Message serializer for GraphSON. Allow users to pass custom reader,
+    writer, and version kwargs for custom serialization. Otherwise,
+    use current GraphSON version as default.
+    """
+
+    # KEEP TRACK OF CURRENT DEFAULTS
+    DEFAULT_READER_CLASS = graphsonV4.GraphSONReader
+    DEFAULT_WRITER_CLASS = graphsonV4.GraphSONWriter
+    DEFAULT_VERSION = b"application/vnd.gremlin-v4.0+json"
+
+    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._graphson_reader = reader
+        if not writer:
+            writer = self.DEFAULT_WRITER_CLASS()
+        self._graphson_writer = writer
+
+    @property
+    def version(self):
+        """Read only property"""
+        return self._version
+
+    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
+        message['gremlin'] =  gremlin
+        return self.finalize_message(message)
+
+    def finalize_message(self, message):
+        message = json.dumps(message)
+        return message
+
+    def deserialize_message(self, message, is_first_chunk=False):
+        if is_first_chunk:
+            msg = json.loads(message if isinstance(message, str) else 
message.decode('utf-8'))
+            return self._graphson_reader.to_object(msg)
+        else:
+            # graphSON does not stream, the first chunk contains all info
+            return ""
 
 """
 GraphBinaryV4
diff --git 
a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV4.py 
b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV4.py
new file mode 100644
index 0000000000..b4aa99bf8e
--- /dev/null
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV4.py
@@ -0,0 +1,730 @@
+# 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 calendar
+import datetime
+import json
+import uuid
+import math
+from collections import OrderedDict
+from decimal import *
+import logging
+from datetime import timedelta
+
+from aenum import Enum
+from isodate import parse_duration, duration_isoformat
+
+from gremlin_python import statics
+from gremlin_python.statics import FloatType, FunctionType, ShortType, 
IntType, LongType, TypeType, DictType, ListType, SetType, SingleByte, 
ByteBufferType, SingleChar
+from gremlin_python.process.traversal import Direction, P, TextP, Traversal, 
Traverser, TraversalStrategy, T
+from gremlin_python.structure.graph import 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 GraphSONTypeType(type):
+    def __new__(mcs, name, bases, dct):
+        cls = super(GraphSONTypeType, mcs).__new__(mcs, name, bases, dct)
+        if not name.startswith('_'):
+            if cls.python_type:
+                _serializers[cls.python_type] = cls
+            if cls.graphson_type:
+                _deserializers[cls.graphson_type] = cls
+        return cls
+
+
+class GraphSONUtil(object):
+    TYPE_KEY = "@type"
+    VALUE_KEY = "@value"
+
+    @classmethod
+    def typed_value(cls, type_name, value, prefix="g"):
+        out = {cls.TYPE_KEY: cls.format_type(prefix, type_name)}
+        if value is not None:
+            out[cls.VALUE_KEY] = value
+        return out
+
+    @classmethod
+    def format_type(cls, prefix, type_name):
+        return "%s:%s" % (prefix, type_name)
+
+
+# Read/Write classes split to follow precedence of the Java API
+class GraphSONWriter(object):
+    def __init__(self, serializer_map=None):
+        """
+        :param serializer_map: map from Python type to serializer instance 
implementing `dictify`
+        """
+        self.serializers = _serializers.copy()
+        if serializer_map:
+            self.serializers.update(serializer_map)
+
+    def write_object(self, objectData):
+        # to JSON
+        return json.dumps(self.to_dict(objectData), separators=(',', ':'))
+
+    def to_dict(self, obj):
+        """
+        Encodes python objects in GraphSON type-tagged dict values
+        """
+        try:
+            return self.serializers[type(obj)].dictify(obj, self)
+        except KeyError:
+            for key, serializer in self.serializers.items():
+                if isinstance(obj, key):
+                    return serializer.dictify(obj, self)
+
+        if isinstance(obj, dict):
+            return dict((self.to_dict(k), self.to_dict(v)) for k, v in 
obj.items())
+        elif isinstance(obj, set):
+            return set([self.to_dict(o) for o in obj])
+        elif isinstance(obj, list):
+            return [self.to_dict(o) for o in obj]
+        else:
+            return obj
+
+
+class GraphSONReader(object):
+    def __init__(self, deserializer_map=None):
+        """
+        :param deserializer_map: map from GraphSON type tag to deserializer 
instance implementing `objectify`
+        """
+        self.deserializers = _deserializers.copy()
+        if deserializer_map:
+            self.deserializers.update(deserializer_map)
+
+    def read_object(self, json_data):
+        # from JSON
+        return self.to_object(json.loads(json_data))
+
+    def to_object(self, obj):
+        """
+        Unpacks GraphSON type-tagged dict values into objects mapped in 
self.deserializers
+        """
+        if isinstance(obj, dict):
+            try:
+                return 
self.deserializers[obj[GraphSONUtil.TYPE_KEY]].objectify(obj[GraphSONUtil.VALUE_KEY],
 self)
+            except KeyError:
+                pass
+            return dict((self.to_object(k), self.to_object(v)) for k, v in 
obj.items())
+        elif isinstance(obj, set):
+            return set([self.to_object(o) for o in obj])
+        elif isinstance(obj, list):
+            return [self.to_object(o) for o in obj]
+        else:
+            return obj
+
+
+class _GraphSONTypeIO(object, metaclass=GraphSONTypeType):
+    python_type = None
+    graphson_type = None
+
+    def dictify(self, obj, writer):
+        raise NotImplementedError()
+
+    def objectify(self, d, reader):
+        raise NotImplementedError()
+
+
+class _BytecodeSerializer(_GraphSONTypeIO):
+    @classmethod
+    def _dictify_instructions(cls, instructions, writer):
+        out = []
+        for instruction in instructions:
+            inst = [instruction[0]]
+            inst.extend(writer.to_dict(arg) for arg in instruction[1:])
+            out.append(inst)
+        return out
+
+    @classmethod
+    def dictify(cls, bytecode, writer):
+        if isinstance(bytecode, Traversal):
+            bytecode = bytecode.bytecode
+        out = {}
+        if bytecode.source_instructions:
+            out["source"] = 
cls._dictify_instructions(bytecode.source_instructions, writer)
+        if bytecode.step_instructions:
+            out["step"] = 
cls._dictify_instructions(bytecode.step_instructions, writer)
+        return GraphSONUtil.typed_value("Bytecode", out)
+
+
+class TraversalSerializer(_BytecodeSerializer):
+    python_type = Traversal
+
+
+class VertexSerializer(_GraphSONTypeIO):
+    python_type = Vertex
+    graphson_type = "g:Vertex"
+
+    @classmethod
+    def dictify(cls, vertex, writer):
+        return GraphSONUtil.typed_value("Vertex", {"id": 
writer.to_dict(vertex.id),
+                                                  "label": 
writer.to_dict([vertex.label])})
+
+
+class EdgeSerializer(_GraphSONTypeIO):
+    python_type = Edge
+    graphson_type = "g:Edge"
+
+    @classmethod
+    def dictify(cls, edge, writer):
+        return GraphSONUtil.typed_value("Edge", {"id": writer.to_dict(edge.id),
+                                                 "label": 
writer.to_dict([edge.label]),
+                                                 "inV": 
writer.to_dict(edge.inV),
+                                                 "outV": 
writer.to_dict(edge.outV)})
+
+                                                # "outV": 
writer.to_dict(edge.outV.id),
+                                                # "outVLabel": 
writer.to_dict([edge.outV.label]),
+                                                # "inV": 
writer.to_dict(edge.inV.id),
+                                                # "inVLabel": 
writer.to_dict([edge.inV.label])})
+
+
+class VertexPropertySerializer(_GraphSONTypeIO):
+    python_type = VertexProperty
+    graphson_type = "g:VertexProperty"
+
+    @classmethod
+    def dictify(cls, vertex_property, writer):
+        return GraphSONUtil.typed_value("VertexProperty", {"id": 
writer.to_dict(vertex_property.id),
+                                                          "label": 
writer.to_dict([vertex_property.label]),
+                                                          "value": 
writer.to_dict(vertex_property.value),
+                                                          "vertex": 
writer.to_dict(vertex_property.vertex.id)})
+
+
+class PropertySerializer(_GraphSONTypeIO):
+    python_type = Property
+    graphson_type = "g:Property"
+
+    @classmethod
+    def dictify(cls, property, writer):
+        elementDict = writer.to_dict(property.element)
+        if elementDict is not None:
+            valueDict = elementDict["@value"]
+            if "outVLabel" in valueDict:
+                del valueDict["outVLabel"]
+            if "inVLabel" in valueDict:
+                del valueDict["inVLabel"]
+            if "properties" in valueDict:
+                del valueDict["properties"]
+            if "value" in valueDict:
+                del valueDict["value"]
+        return GraphSONUtil.typed_value("Property", {"key": 
writer.to_dict(property.key),
+                                                    "value": 
writer.to_dict(property.value),
+                                                    "element": elementDict})
+
+
+class TraversalStrategySerializer(_GraphSONTypeIO):
+    python_type = TraversalStrategy
+
+    @classmethod
+    def dictify(cls, strategy, writer):
+        configuration = {}
+        for key in strategy.configuration:
+            configuration[key] = writer.to_dict(strategy.configuration[key])
+        return GraphSONUtil.typed_value(strategy.strategy_name, configuration)
+
+
+class TraverserIO(_GraphSONTypeIO):
+    python_type = Traverser
+    graphson_type = "g:Traverser"
+
+    @classmethod
+    def dictify(cls, traverser, writer):
+        return GraphSONUtil.typed_value("Traverser", {"value": 
writer.to_dict(traverser.object),
+                                                     "bulk": 
writer.to_dict(traverser.bulk)})
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return Traverser(reader.to_object(d["value"]),
+                         reader.to_object(d["bulk"]))
+
+
+class EnumSerializer(_GraphSONTypeIO):
+    python_type = Enum
+
+    @classmethod
+    def dictify(cls, enum, _):
+        return 
GraphSONUtil.typed_value(SymbolUtil.to_camel_case(type(enum).__name__),
+                                        
SymbolUtil.to_camel_case(str(enum.name)))
+
+
+class PSerializer(_GraphSONTypeIO):
+    python_type = P
+
+    @classmethod
+    def dictify(cls, p, writer):
+        out = {"predicate": p.operator,
+               "value": [writer.to_dict(p.value), writer.to_dict(p.other)] if 
p.other is not None else
+               writer.to_dict(p.value)}
+        return GraphSONUtil.typed_value("P", out)
+
+
+class TextPSerializer(_GraphSONTypeIO):
+    python_type = TextP
+
+    @classmethod
+    def dictify(cls, p, writer):
+        out = {"predicate": p.operator,
+               "value": [writer.to_dict(p.value), writer.to_dict(p.other)] if 
p.other is not None else
+               writer.to_dict(p.value)}
+        return GraphSONUtil.typed_value("TextP", out)
+
+
+class TypeSerializer(_GraphSONTypeIO):
+    python_type = TypeType
+
+    @classmethod
+    def dictify(cls, typ, writer):
+        return writer.to_dict(typ())
+
+
+class UUIDIO(_GraphSONTypeIO):
+    python_type = uuid.UUID
+    graphson_type = "g:UUID"
+    graphson_base_type = "UUID"
+
+    @classmethod
+    def dictify(cls, obj, writer):
+        return GraphSONUtil.typed_value(cls.graphson_base_type, str(obj))
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return cls.python_type(d)
+
+
+class DateIO(_GraphSONTypeIO):
+    python_type = datetime.datetime
+    graphson_type = "g:Date"
+    graphson_base_type = "Date"
+
+    @classmethod
+    def dictify(cls, obj, writer):
+        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))
+        return GraphSONUtil.typed_value(cls.graphson_base_type, ts)
+
+    @classmethod
+    def objectify(cls, ts, reader):
+        # Python timestamp expects seconds
+        return datetime.datetime.utcfromtimestamp(ts / 1000.0)
+
+
+class _NumberIO(_GraphSONTypeIO):
+    @classmethod
+    def dictify(cls, n, writer):
+        if isinstance(n, bool):  # because isinstance(False, int) and 
isinstance(True, int)
+            return n
+        return GraphSONUtil.typed_value(cls.graphson_base_type, n)
+
+    @classmethod
+    def objectify(cls, v, _):
+        return cls.python_type(v)
+
+
+class ListIO(_GraphSONTypeIO):
+    python_type = ListType
+    graphson_type = "g:List"
+
+    @classmethod
+    def dictify(cls, l, writer):
+        new_list = []
+        for obj in l:
+            new_list.append(writer.to_dict(obj))
+        return GraphSONUtil.typed_value("List", new_list)
+
+    @classmethod
+    def objectify(cls, l, reader):
+        new_list = []
+        for obj in l:
+            new_list.append(reader.to_object(obj))
+        return new_list
+
+
+class SetIO(_GraphSONTypeIO):
+    python_type = SetType
+    graphson_type = "g:Set"
+
+    @classmethod
+    def dictify(cls, s, writer):
+        new_list = []
+        for obj in s:
+            new_list.append(writer.to_dict(obj))
+        return GraphSONUtil.typed_value("Set", new_list)
+
+    @classmethod
+    def objectify(cls, s, reader):
+        """
+        By default, returns a python set
+
+        In case Java returns numeric values of different types which
+        python don't recognize, coerce and return a list.
+        See comments of TINKERPOP-1844 for more details
+        """
+        new_list = [reader.to_object(obj) for obj in s]
+        new_set = set(new_list)
+        if len(new_list) != len(new_set):
+            log.warning("Coercing g:Set to list due to java numeric values. "
+                        "See TINKERPOP-1844 for more details.")
+            return new_list
+
+        return new_set
+
+
+class MapType(_GraphSONTypeIO):
+    python_type = DictType
+    graphson_type = "g:Map"
+
+    @classmethod
+    def dictify(cls, d, writer):
+        l = []
+        for key in d:
+            l.append(writer.to_dict(key))
+            l.append(writer.to_dict(d[key]))
+        return GraphSONUtil.typed_value("Map", l)
+
+    @classmethod
+    def objectify(cls, l, reader):
+        new_dict = {}
+        if len(l) > 0:
+            x = 0
+            while x < len(l):
+                new_dict[HashableDict.of(reader.to_object(l[x]))] = 
reader.to_object(l[x + 1])
+                x = x + 2
+        return new_dict
+
+
+class BulkSetIO(_GraphSONTypeIO):
+    graphson_type = "g:BulkSet"
+
+    @classmethod
+    def objectify(cls, l, reader):
+        new_list = []
+
+        # this approach basically mimics what currently existed in 3.3.4 and 
prior versions where BulkSet is
+        # basically just coerced to list. the limitation here is that if the 
value of a bulk exceeds the size of
+        # a list (into the long space) then stuff won't work nice.
+        if len(l) > 0:
+            x = 0
+            while x < len(l):
+                obj = reader.to_object(l[x])
+                bulk = reader.to_object(l[x + 1])
+                for y in range(bulk):
+                    new_list.append(obj)
+                x = x + 2
+        return new_list
+
+
+class FloatIO(_NumberIO):
+    python_type = FloatType
+    graphson_type = "g:Float"
+    graphson_base_type = "Float"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        if isinstance(n, bool):  # because isinstance(False, int) and 
isinstance(True, int)
+            return n
+        elif math.isnan(n):
+            return GraphSONUtil.typed_value(cls.graphson_base_type, "NaN")
+        elif math.isinf(n) and n > 0:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, "Infinity")
+        elif math.isinf(n) and n < 0:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, 
"-Infinity")
+        else:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, n)
+
+    @classmethod
+    def objectify(cls, v, _):
+        if isinstance(v, str):
+            if v == 'NaN':
+                return float('nan')
+            elif v == "Infinity":
+                return float('inf')
+            elif v == "-Infinity":
+                return float('-inf')
+
+        return cls.python_type(v)
+
+
+class BigDecimalIO(_NumberIO):
+    python_type = Decimal
+    graphson_type = "gx:BigDecimal"
+    graphson_base_type = "BigDecimal"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        if isinstance(n, bool):  # because isinstance(False, int) and 
isinstance(True, int)
+            return n
+        elif math.isnan(n):
+            return GraphSONUtil.typed_value(cls.graphson_base_type, "NaN", 
"gx")
+        elif math.isinf(n) and n > 0:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, 
"Infinity", "gx")
+        elif math.isinf(n) and n < 0:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, 
"-Infinity", "gx")
+        else:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, str(n), 
"gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        if isinstance(v, str):
+            if v == 'NaN':
+                return Decimal('nan')
+            elif v == "Infinity":
+                return Decimal('inf')
+            elif v == "-Infinity":
+                return Decimal('-inf')
+
+        return Decimal(v)
+
+
+class DoubleIO(FloatIO):
+    graphson_type = "g:Double"
+    graphson_base_type = "Double"
+
+
+class Int64IO(_NumberIO):
+    python_type = LongType
+    graphson_type = "g:Int64"
+    graphson_base_type = "Int64"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        # if we exceed Java long range then we need a BigInteger
+        if isinstance(n, bool):
+            return n
+        elif n < -9223372036854775808 or n > 9223372036854775807:
+            return GraphSONUtil.typed_value("BigInteger", str(n), "gx")
+        else:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, n)
+
+
+class BigIntegerIO(Int64IO):
+    graphson_type = "gx:BigInteger"
+
+
+class Int32IO(Int64IO):
+    python_type = IntType
+    graphson_type = "g:Int32"
+    graphson_base_type = "Int32"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        # if we exceed Java int range then we need a long
+        if isinstance(n, bool):
+            return n
+        elif n < -9223372036854775808 or n > 9223372036854775807:
+            return GraphSONUtil.typed_value("BigInteger", str(n), "gx")
+        elif n < -2147483648 or n > 2147483647:
+            return GraphSONUtil.typed_value("Int64", n)
+        else:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, n)
+
+
+class Int16IO(Int64IO):
+    python_type = ShortType
+    graphson_type = "gx:Int16"
+    graphson_base_type = "Int16"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        # if we exceed Java int range then we need a long
+        if isinstance(n, bool):
+            return n
+        elif n < -9223372036854775808 or n > 9223372036854775807:
+            return GraphSONUtil.typed_value("BigInteger", str(n), "gx")
+        elif n < -2147483648 or n > 2147483647:
+            return GraphSONUtil.typed_value("Int64", n)
+        elif n < -32768 or n > 32767:
+            return GraphSONUtil.typed_value("Int32", n)
+        else:
+            return GraphSONUtil.typed_value(cls.graphson_base_type, n, "gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        return int.__new__(ShortType, v)
+
+
+class ByteIO(_NumberIO):
+    python_type = SingleByte
+    graphson_type = "gx:Byte"
+    graphson_base_type = "Byte"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        if isinstance(n, bool):  # because isinstance(False, int) and 
isinstance(True, int)
+            return n
+        return GraphSONUtil.typed_value(cls.graphson_base_type, n, "gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        return int.__new__(SingleByte, v)
+
+
+class ByteBufferIO(_GraphSONTypeIO):
+    python_type = ByteBufferType
+    graphson_type = "gx:ByteBuffer"
+    graphson_base_type = "ByteBuffer"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        return GraphSONUtil.typed_value(cls.graphson_base_type, "".join(chr(x) 
for x in n), "gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        return cls.python_type(v, "utf8")
+
+
+class CharIO(_GraphSONTypeIO):
+    python_type = SingleChar
+    graphson_type = "gx:Char"
+    graphson_base_type = "Char"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        return GraphSONUtil.typed_value(cls.graphson_base_type, n, "gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        return str.__new__(SingleChar, v)
+
+
+class DurationIO(_GraphSONTypeIO):
+    python_type = timedelta
+    graphson_type = "gx:Duration"
+    graphson_base_type = "Duration"
+
+    @classmethod
+    def dictify(cls, n, writer):
+        return GraphSONUtil.typed_value(cls.graphson_base_type, 
duration_isoformat(n), "gx")
+
+    @classmethod
+    def objectify(cls, v, _):
+        return parse_duration(v)
+
+
+class VertexDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:Vertex"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        properties = None
+        if "properties" in d:
+            properties = reader.to_object(d["properties"])
+            if properties is not None:
+                properties = [item for sublist in properties.values() for item 
in sublist]
+        return Vertex(reader.to_object(d["id"]), d.get("label", "vertex")[0], 
properties)
+
+
+class EdgeDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:Edge"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        properties = None
+        if "properties" in d:
+            properties = reader.to_object(d["properties"])
+            if properties is not None:
+                properties = list(properties.values())
+        return Edge(reader.to_object(d["id"]),
+                    Vertex(reader.to_object(d["outV"]), d.get("outVLabel", 
"vertex")[0]),
+                    d.get("label", "edge")[0],
+                    Vertex(reader.to_object(d["inV"]), d.get("inVLabel", 
"vertex")[0]),
+                    properties)
+
+
+class VertexPropertyDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:VertexProperty"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        properties = None
+        if "properties" in d:
+            properties = reader.to_object(d["properties"])
+            if properties is not None:
+                properties = list(map(lambda x: Property(x[0], x[1], None), 
properties.items()))
+        vertex = Vertex(reader.to_object(d.get("vertex"))) if "vertex" in d 
else None
+        return VertexProperty(reader.to_object(d["id"]),
+                              d["label"][0],
+                              reader.to_object(d["value"]),
+                              vertex,
+                              properties)
+
+
+class PropertyDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:Property"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        element = reader.to_object(d["element"]) if "element" in d else None
+        return Property(d["key"], reader.to_object(d["value"]), element)
+
+
+class PathDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:Path"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return Path(reader.to_object(d["labels"]), 
reader.to_object(d["objects"]))
+
+
+class TDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:T"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return T[d]
+
+
+class DirectionIO(_GraphSONTypeIO):
+    graphson_type = "g:Direction"
+    graphson_base_type = "Direction"
+    python_type = Direction
+
+    @classmethod
+    def dictify(cls, d, writer):
+        return GraphSONUtil.typed_value(cls.graphson_base_type, d.name, "g")
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return Direction[d]
+
+
+class TraversalMetricsDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:TraversalMetrics"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return reader.to_object(d)
+
+
+class MetricsDeserializer(_GraphSONTypeIO):
+    graphson_type = "g:Metrics"
+
+    @classmethod
+    def objectify(cls, d, reader):
+        return reader.to_object(d)
diff --git 
a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py 
b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
index 607501935a..c2cb65af0f 100644
--- 
a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
+++ 
b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
@@ -20,6 +20,7 @@ import os
 from datetime import datetime
 
 from gremlin_python import statics
+from gremlin_python.driver.driver_remote_connection import 
DriverRemoteConnection
 from gremlin_python.statics import long
 from gremlin_python.process.traversal import TraversalStrategy, P, Order, T, 
DT, Parameter, Cardinality
 from gremlin_python.process.graph_traversal import __
@@ -28,6 +29,7 @@ from gremlin_python.structure.graph import Vertex
 from gremlin_python.process.strategies import SubgraphStrategy, SeedStrategy
 from gremlin_python.structure.io.util import HashableDict
 from gremlin_python.driver.protocol import GremlinServerError
+from gremlin_python.driver import serializer
 
 gremlin_server_url = os.environ.get('GREMLIN_SERVER_URL_HTTP', 
'http://localhost:{}/')
 test_no_auth_url = gremlin_server_url.format(45940)
@@ -35,6 +37,26 @@ test_no_auth_url = gremlin_server_url.format(45940)
 
 class TestDriverRemoteConnection(object):
 
+    # this is a temporary test for basic graphSONV4 connectivity, once all 
types are implemented, enable graphSON testing
+    # in conftest.py and remove this
+    def test_graphSONV4_temp(self):
+        remote_conn = DriverRemoteConnection(test_no_auth_url, 'gmodern',
+                                             
message_serializer=serializer.GraphSONSerializerV4())
+        g = traversal().with_(remote_conn)
+        assert long(6) == g.V().count().to_list()[0]
+        # #
+        assert 10 == g.V().repeat(__.both()).times(5)[0:10].count().next()
+        assert 1 == g.V().repeat(__.both()).times(5)[0:1].count().next()
+        assert 0 == g.V().repeat(__.both()).times(5)[0:0].count().next()
+        assert 4 == g.V()[2:].count().next()
+        assert 2 == g.V()[:2].count().next()
+        # #
+        results = g.with_side_effect('a', ['josh', 
'peter']).V(1).out('created').in_('created').values('name').where(
+            P.within('a')).to_list()
+        assert 2 == len(results)
+        assert 'josh' in results
+        assert 'peter' in results
+
     def test_bulked_request_option(self, remote_connection):
         g = traversal().with_(remote_connection)
         result = g.inject(1,2,3,2,1).to_list()

Reply via email to