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

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

commit 24535368205f01234804f29904b5cbb917e24a28
Merge: 699ea53 a3cefa0
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Wed Apr 29 08:23:24 2020 -0400

    Merge branch '3.4-dev'

 CHANGELOG.asciidoc                                           |  1 +
 .../main/python/gremlin_python/structure/io/graphsonV2d0.py  | 12 ++++++++++++
 .../main/python/gremlin_python/structure/io/graphsonV3d0.py  | 11 +++++++++++
 gremlin-python/src/main/python/tests/driver/test_client.py   | 11 +++++++++++
 .../src/main/python/tests/structure/io/test_graphsonV2d0.py  | 11 +++++------
 .../src/main/python/tests/structure/io/test_graphsonV3d0.py  | 11 +++++------
 6 files changed, 45 insertions(+), 12 deletions(-)

diff --cc 
gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
index dec50b0,0000000..20d186e
mode 100644,000000..100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py
@@@ -1,620 -1,0 +1,632 @@@
 +#
 +# 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 *
 +from datetime import timedelta
 +
 +import six
 +from aenum import Enum
 +from isodate import parse_duration, duration_isoformat
 +
 +from gremlin_python import statics
 +from gremlin_python.statics import FloatType, FunctionType, IntType, 
LongType, TypeType, SingleByte, ByteBufferType, SingleChar
 +from gremlin_python.process.traversal import Binding, Bytecode, P, TextP, 
Traversal, Traverser, TraversalStrategy
 +from gremlin_python.structure.graph import Edge, Property, Vertex, 
VertexProperty, Path
 +
 +# 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 typedValue(cls, type_name, value, prefix="g"):
 +        out = {cls.TYPE_KEY: cls.formatType(prefix, type_name)}
 +        if value is not None:
 +            out[cls.VALUE_KEY] = value
 +        return out
 +
 +    @classmethod
 +    def formatType(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 writeObject(self, objectData):
 +        # to JSON
 +        return json.dumps(self.toDict(objectData), separators=(',', ':'))
 +
 +    def toDict(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)
 +
 +        # list and map are treated as normal json objs (could be isolated 
serializers)
 +        if isinstance(obj, (list, set)):
 +            return [self.toDict(o) for o in obj]
 +        elif isinstance(obj, dict):
 +            return dict((self.toDict(k), self.toDict(v)) for k, v in 
obj.items())
 +        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 readObject(self, jsonData):
 +        # from JSON
 +        return self.toObject(json.loads(jsonData))
 +
 +    def toObject(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
 +            # list and map are treated as normal json objs (could be isolated 
deserializers)
 +            return dict((self.toObject(k), self.toObject(v)) for k, v in 
obj.items())
 +        elif isinstance(obj, list):
 +            return [self.toObject(o) for o in obj]
 +        else:
 +            return obj
 +
 +
 +@six.add_metaclass(GraphSONTypeType)
 +class _GraphSONTypeIO(object):
 +    python_type = None
 +    graphson_type = None
 +
 +    symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and",
 +                 "or_": "or", "is_": "is", "not_": "not", "from_": "from",
 +                 "set_": "set", "list_": "list", "all_": "all", "with_": 
"with",
 +                 "filter_": "filter", "id_": "id", "max_": "max", "min_": 
"min", "sum_": "sum"}
 +
 +    @classmethod
 +    def unmangleKeyword(cls, symbol):
 +        return cls.symbolMap.get(symbol, symbol)
 +
 +    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.toDict(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.typedValue("Bytecode", out)
 +
 +class TraversalSerializer(_BytecodeSerializer):
 +    python_type = Traversal
 +
 +
 +class BytecodeSerializer(_BytecodeSerializer):
 +    python_type = Bytecode
 +
 +
 +class VertexSerializer(_GraphSONTypeIO):
 +    python_type = Vertex
 +    graphson_type = "g:Vertex"
 +
 +    @classmethod
 +    def dictify(cls, vertex, writer):
 +        return GraphSONUtil.typedValue("Vertex", {"id": 
writer.toDict(vertex.id),
 +                                                  "label": 
writer.toDict(vertex.label)})
 +
 +
 +class EdgeSerializer(_GraphSONTypeIO):
 +    python_type = Edge
 +    graphson_type = "g:Edge"
 +
 +    @classmethod
 +    def dictify(cls, edge, writer):
 +        return GraphSONUtil.typedValue("Edge", {"id": writer.toDict(edge.id),
 +                                                "outV": 
writer.toDict(edge.outV.id),
 +                                                "outVLabel": 
writer.toDict(edge.outV.label),
 +                                                "label": 
writer.toDict(edge.label),
 +                                                "inV": 
writer.toDict(edge.inV.id),
 +                                                "inVLabel": 
writer.toDict(edge.inV.label)})
 +
 +
 +class VertexPropertySerializer(_GraphSONTypeIO):
 +    python_type = VertexProperty
 +    graphson_type = "g:VertexProperty"
 +
 +    @classmethod
 +    def dictify(cls, vertex_property, writer):
 +        return GraphSONUtil.typedValue("VertexProperty", {"id": 
writer.toDict(vertex_property.id),
 +                                                          "label": 
writer.toDict(vertex_property.label),
 +                                                          "value": 
writer.toDict(vertex_property.value),
 +                                                          "vertex": 
writer.toDict(vertex_property.vertex.id)})
 +
 +
 +class PropertySerializer(_GraphSONTypeIO):
 +    python_type = Property
 +    graphson_type = "g:Property"
 +
 +    @classmethod
 +    def dictify(cls, property, writer):
 +        elementDict = writer.toDict(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.typedValue("Property", {"key": 
writer.toDict(property.key),
 +                                                    "value": 
writer.toDict(property.value),
 +                                                    "element": elementDict})
 +
 +
 +class TraversalStrategySerializer(_GraphSONTypeIO):
 +    python_type = TraversalStrategy
 +
 +    @classmethod
 +    def dictify(cls, strategy, writer):
 +        return GraphSONUtil.typedValue(strategy.strategy_name, 
writer.toDict(strategy.configuration))
 +
 +
 +class TraverserIO(_GraphSONTypeIO):
 +    python_type = Traverser
 +    graphson_type = "g:Traverser"
 +
 +    @classmethod
 +    def dictify(cls, traverser, writer):
 +        return GraphSONUtil.typedValue("Traverser", {"value": 
writer.toDict(traverser.object),
 +                                                     "bulk": 
writer.toDict(traverser.bulk)})
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return Traverser(reader.toObject(d["value"]),
 +                         reader.toObject(d["bulk"]))
 +
 +
 +class EnumSerializer(_GraphSONTypeIO):
 +    python_type = Enum
 +
 +    @classmethod
 +    def dictify(cls, enum, _):
 +        return 
GraphSONUtil.typedValue(cls.unmangleKeyword(type(enum).__name__),
 +                                       cls.unmangleKeyword(str(enum.name)))
 +
 +
 +class PSerializer(_GraphSONTypeIO):
 +    python_type = P
 +
 +    @classmethod
 +    def dictify(cls, p, writer):
 +        out = {"predicate": p.operator,
 +               "value": [writer.toDict(p.value), writer.toDict(p.other)] if 
p.other is not None else
 +               writer.toDict(p.value)}
 +        return GraphSONUtil.typedValue("P", out)
 +
 +
 +class TextPSerializer(_GraphSONTypeIO):
 +    python_type = TextP
 +
 +    @classmethod
 +    def dictify(cls, p, writer):
 +        out = {"predicate": p.operator,
 +               "value": [writer.toDict(p.value), writer.toDict(p.other)] if 
p.other is not None else
 +               writer.toDict(p.value)}
 +        return GraphSONUtil.typedValue("TextP", out)
 +
 +
 +class BindingSerializer(_GraphSONTypeIO):
 +    python_type = Binding
 +
 +    @classmethod
 +    def dictify(cls, binding, writer):
 +        out = {"key": binding.key,
 +               "value": writer.toDict(binding.value)}
 +        return GraphSONUtil.typedValue("Binding", out)
 +
 +
 +class LambdaSerializer(_GraphSONTypeIO):
 +    python_type = FunctionType
 +
 +    @classmethod
 +    def dictify(cls, lambda_object, writer):
 +        lambda_result = lambda_object()
 +        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]
 +        out = {"script": script,
 +               "language": language}
 +        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("->")]
 +            out["arguments"] = 2 if "," in args else 1
 +        else:
 +            out["arguments"] = -1
 +
 +        return GraphSONUtil.typedValue("Lambda", out)
 +
 +
 +class TypeSerializer(_GraphSONTypeIO):
 +    python_type = TypeType
 +
 +    @classmethod
 +    def dictify(cls, typ, writer):
 +        return writer.toDict(typ())
 +
 +
 +class UUIDIO(_GraphSONTypeIO):
 +    python_type = uuid.UUID
 +    graphson_type = "g:UUID"
 +    graphson_base_type = "UUID"
 +
 +    @classmethod
 +    def dictify(cls, obj, writer):
 +        return GraphSONUtil.typedValue(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.typedValue(cls.graphson_base_type, ts)
 +
 +    @classmethod
 +    def objectify(cls, ts, reader):
 +        # Python timestamp expects seconds
 +        return datetime.datetime.utcfromtimestamp(ts / 1000.0)
 +
 +
 +# Based on current implementation, this class must always be declared before 
FloatIO.
 +# Seems pretty fragile for future maintainers. Maybe look into this.
 +class TimestampIO(_GraphSONTypeIO):
 +    """A timestamp in Python is type float"""
 +    python_type = statics.timestamp
 +    graphson_type = "g:Timestamp"
 +    graphson_base_type = "Timestamp"
 +
 +    @classmethod
 +    def dictify(cls, obj, writer):
 +        # Java timestamp expects milliseconds integer
 +        # Have to use int because of legacy Python
 +        ts = int(round(obj * 1000))
 +        return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
 +
 +    @classmethod
 +    def objectify(cls, ts, reader):
 +        # Python timestamp expects seconds
 +        return cls.python_type(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.typedValue(cls.graphson_base_type, n)
 +
 +    @classmethod
 +    def objectify(cls, v, _):
 +        return cls.python_type(v)
 +
 +
 +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.typedValue(cls.graphson_base_type, "NaN")
 +        elif math.isinf(n) and n > 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, "Infinity")
 +        elif math.isinf(n) and n < 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"-Infinity")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue(cls.graphson_base_type, "NaN", 
"gx")
 +        elif math.isinf(n) and n > 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"Infinity", "gx")
 +        elif math.isinf(n) and n < 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"-Infinity", "gx")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue("BigInteger", str(n), "gx")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue("BigInteger", str(n), "gx")
++        elif n < -2147483648 or n > 2147483647:
++            return GraphSONUtil.typedValue("Int64", n)
++        else:
++            return GraphSONUtil.typedValue(cls.graphson_base_type, n)
++
 +
 +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.typedValue(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.typedValue(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.typedValue(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.typedValue(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):
 +        return Vertex(reader.toObject(d["id"]), d.get("label", "vertex"))
 +
 +
 +class EdgeDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Edge"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return Edge(reader.toObject(d["id"]),
 +                    Vertex(reader.toObject(d["outV"]), d.get("outVLabel", 
"vertex")),
 +                    d.get("label", "edge"),
 +                    Vertex(reader.toObject(d["inV"]), d.get("inVLabel", 
"vertex")))
 +
 +
 +class VertexPropertyDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:VertexProperty"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        vertex = Vertex(reader.toObject(d.get("vertex"))) if "vertex" in d 
else None
 +        return VertexProperty(reader.toObject(d["id"]),
 +                              d["label"],
 +                              reader.toObject(d["value"]),
 +                              vertex)
 +
 +
 +class PropertyDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Property"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        element = reader.toObject(d["element"]) if "element" in d else None
 +        return Property(d["key"], reader.toObject(d["value"]), element)
 +
 +
 +class PathDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Path"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        labels = [set(label) for label in d["labels"]]
 +        objects = [reader.toObject(o) for o in d["objects"]]
 +        return Path(labels, objects)
 +
 +
 +class TraversalMetricsDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:TraversalMetrics"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return reader.toObject(d)
 +
 +
 +class MetricsDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Metrics"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return reader.toObject(d)
diff --cc 
gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
index 9213d7a,0000000..21c31ba
mode 100644,000000..100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
@@@ -1,739 -1,0 +1,750 @@@
 +# 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
 +
 +import six
 +from aenum import Enum
 +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
 +from gremlin_python.process.traversal import Binding, Bytecode, Direction, P, 
TextP, Traversal, Traverser, TraversalStrategy, T
 +from gremlin_python.structure.graph import Edge, Property, Vertex, 
VertexProperty, Path
 +
 +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 typedValue(cls, type_name, value, prefix="g"):
 +        out = {cls.TYPE_KEY: cls.formatType(prefix, type_name)}
 +        if value is not None:
 +            out[cls.VALUE_KEY] = value
 +        return out
 +
 +    @classmethod
 +    def formatType(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 writeObject(self, objectData):
 +        # to JSON
 +        return json.dumps(self.toDict(objectData), separators=(',', ':'))
 +
 +    def toDict(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.toDict(k), self.toDict(v)) for k, v in 
obj.items())
 +        elif isinstance(obj, set):
 +            return set([self.toDict(o) for o in obj])
 +        elif isinstance(obj, list):
 +            return [self.toDict(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 readObject(self, jsonData):
 +        # from JSON
 +        return self.toObject(json.loads(jsonData))
 +
 +    def toObject(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.toObject(k), self.toObject(v)) for k, v in 
obj.items())
 +        elif isinstance(obj, set):
 +            return set([self.toObject(o) for o in obj])
 +        elif isinstance(obj, list):
 +            return [self.toObject(o) for o in obj]
 +        else:
 +            return obj
 +
 +
 +@six.add_metaclass(GraphSONTypeType)
 +class _GraphSONTypeIO(object):
 +    python_type = None
 +    graphson_type = None
 +
 +    symbolMap = {"global_": "global", "as_": "as", "in_": "in", "and_": "and",
 +                 "or_": "or", "is_": "is", "not_": "not", "from_": "from",
 +                 "set_": "set", "list_": "list", "all_": "all", "with_": 
"with",
 +                 "filter_": "filter", "id_": "id", "max_": "max", "min_": 
"min", "sum_": "sum"}
 +
 +    @classmethod
 +    def unmangleKeyword(cls, symbol):
 +        return cls.symbolMap.get(symbol, symbol)
 +
 +    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.toDict(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.typedValue("Bytecode", out)
 +
 +
 +class TraversalSerializer(_BytecodeSerializer):
 +    python_type = Traversal
 +
 +
 +class BytecodeSerializer(_BytecodeSerializer):
 +    python_type = Bytecode
 +
 +
 +class VertexSerializer(_GraphSONTypeIO):
 +    python_type = Vertex
 +    graphson_type = "g:Vertex"
 +
 +    @classmethod
 +    def dictify(cls, vertex, writer):
 +        return GraphSONUtil.typedValue("Vertex", {"id": 
writer.toDict(vertex.id),
 +                                                  "label": 
writer.toDict(vertex.label)})
 +
 +
 +class EdgeSerializer(_GraphSONTypeIO):
 +    python_type = Edge
 +    graphson_type = "g:Edge"
 +
 +    @classmethod
 +    def dictify(cls, edge, writer):
 +        return GraphSONUtil.typedValue("Edge", {"id": writer.toDict(edge.id),
 +                                                "outV": 
writer.toDict(edge.outV.id),
 +                                                "outVLabel": 
writer.toDict(edge.outV.label),
 +                                                "label": 
writer.toDict(edge.label),
 +                                                "inV": 
writer.toDict(edge.inV.id),
 +                                                "inVLabel": 
writer.toDict(edge.inV.label)})
 +
 +
 +class VertexPropertySerializer(_GraphSONTypeIO):
 +    python_type = VertexProperty
 +    graphson_type = "g:VertexProperty"
 +
 +    @classmethod
 +    def dictify(cls, vertex_property, writer):
 +        return GraphSONUtil.typedValue("VertexProperty", {"id": 
writer.toDict(vertex_property.id),
 +                                                          "label": 
writer.toDict(vertex_property.label),
 +                                                          "value": 
writer.toDict(vertex_property.value),
 +                                                          "vertex": 
writer.toDict(vertex_property.vertex.id)})
 +
 +
 +class PropertySerializer(_GraphSONTypeIO):
 +    python_type = Property
 +    graphson_type = "g:Property"
 +
 +    @classmethod
 +    def dictify(cls, property, writer):
 +        elementDict = writer.toDict(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.typedValue("Property", {"key": 
writer.toDict(property.key),
 +                                                    "value": 
writer.toDict(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.toDict(strategy.configuration[key])
 +        return GraphSONUtil.typedValue(strategy.strategy_name, configuration)
 +
 +
 +class TraverserIO(_GraphSONTypeIO):
 +    python_type = Traverser
 +    graphson_type = "g:Traverser"
 +
 +    @classmethod
 +    def dictify(cls, traverser, writer):
 +        return GraphSONUtil.typedValue("Traverser", {"value": 
writer.toDict(traverser.object),
 +                                                     "bulk": 
writer.toDict(traverser.bulk)})
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return Traverser(reader.toObject(d["value"]),
 +                         reader.toObject(d["bulk"]))
 +
 +
 +class EnumSerializer(_GraphSONTypeIO):
 +    python_type = Enum
 +
 +    @classmethod
 +    def dictify(cls, enum, _):
 +        return 
GraphSONUtil.typedValue(cls.unmangleKeyword(type(enum).__name__),
 +                                       cls.unmangleKeyword(str(enum.name)))
 +
 +
 +class PSerializer(_GraphSONTypeIO):
 +    python_type = P
 +
 +    @classmethod
 +    def dictify(cls, p, writer):
 +        out = {"predicate": p.operator,
 +               "value": [writer.toDict(p.value), writer.toDict(p.other)] if 
p.other is not None else
 +               writer.toDict(p.value)}
 +        return GraphSONUtil.typedValue("P", out)
 +
 +
 +class TextPSerializer(_GraphSONTypeIO):
 +    python_type = TextP
 +
 +    @classmethod
 +    def dictify(cls, p, writer):
 +        out = {"predicate": p.operator,
 +               "value": [writer.toDict(p.value), writer.toDict(p.other)] if 
p.other is not None else
 +               writer.toDict(p.value)}
 +        return GraphSONUtil.typedValue("TextP", out)
 +
 +
 +class BindingSerializer(_GraphSONTypeIO):
 +    python_type = Binding
 +
 +    @classmethod
 +    def dictify(cls, binding, writer):
 +        out = {"key": binding.key,
 +               "value": writer.toDict(binding.value)}
 +        return GraphSONUtil.typedValue("Binding", out)
 +
 +
 +class LambdaSerializer(_GraphSONTypeIO):
 +    python_type = FunctionType
 +
 +    @classmethod
 +    def dictify(cls, lambda_object, writer):
 +        lambda_result = lambda_object()
 +        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]
 +        out = {"script": script,
 +               "language": language}
 +        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("->")]
 +            out["arguments"] = 2 if "," in args else 1
 +        else:
 +            out["arguments"] = -1
 +
 +        return GraphSONUtil.typedValue("Lambda", out)
 +
 +
 +class TypeSerializer(_GraphSONTypeIO):
 +    python_type = TypeType
 +
 +    @classmethod
 +    def dictify(cls, typ, writer):
 +        return writer.toDict(typ())
 +
 +
 +class UUIDIO(_GraphSONTypeIO):
 +    python_type = uuid.UUID
 +    graphson_type = "g:UUID"
 +    graphson_base_type = "UUID"
 +
 +    @classmethod
 +    def dictify(cls, obj, writer):
 +        return GraphSONUtil.typedValue(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.typedValue(cls.graphson_base_type, ts)
 +
 +    @classmethod
 +    def objectify(cls, ts, reader):
 +        # Python timestamp expects seconds
 +        return datetime.datetime.utcfromtimestamp(ts / 1000.0)
 +
 +
 +# Based on current implementation, this class must always be declared before 
FloatIO.
 +# Seems pretty fragile for future maintainers. Maybe look into this.
 +class TimestampIO(_GraphSONTypeIO):
 +    """A timestamp in Python is type float"""
 +    python_type = statics.timestamp
 +    graphson_type = "g:Timestamp"
 +    graphson_base_type = "Timestamp"
 +
 +    @classmethod
 +    def dictify(cls, obj, writer):
 +        # Java timestamp expects milliseconds integer
 +        # Have to use int because of legacy Python
 +        ts = int(round(obj * 1000))
 +        return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
 +
 +    @classmethod
 +    def objectify(cls, ts, reader):
 +        # Python timestamp expects seconds
 +        return cls.python_type(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.typedValue(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.toDict(obj))
 +        return GraphSONUtil.typedValue("List", new_list)
 +
 +    @classmethod
 +    def objectify(cls, l, reader):
 +        new_list = []
 +        for obj in l:
 +            new_list.append(reader.toObject(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.toDict(obj))
 +        return GraphSONUtil.typedValue("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.toObject(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.toDict(key))
 +            l.append(writer.toDict(d[key]))
 +        return GraphSONUtil.typedValue("Map", l)
 +
 +    @classmethod
 +    def objectify(cls, l, reader):
 +        new_dict = {}
 +        if len(l) > 0:
 +            x = 0
 +            while x < len(l):
 +                new_dict[reader.toObject(l[x])] = reader.toObject(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.toObject(l[x])
 +                bulk = reader.toObject(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.typedValue(cls.graphson_base_type, "NaN")
 +        elif math.isinf(n) and n > 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, "Infinity")
 +        elif math.isinf(n) and n < 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"-Infinity")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue(cls.graphson_base_type, "NaN", 
"gx")
 +        elif math.isinf(n) and n > 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"Infinity", "gx")
 +        elif math.isinf(n) and n < 0:
 +            return GraphSONUtil.typedValue(cls.graphson_base_type, 
"-Infinity", "gx")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue("BigInteger", str(n), "gx")
 +        else:
 +            return GraphSONUtil.typedValue(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.typedValue("BigInteger", str(n), "gx")
++        elif n < -2147483648 or n > 2147483647:
++            return GraphSONUtil.typedValue("Int64", n)
++        else:
++            return GraphSONUtil.typedValue(cls.graphson_base_type, n)
 +
 +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.typedValue(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.typedValue(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.typedValue(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.typedValue(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):
 +        return Vertex(reader.toObject(d["id"]), d.get("label", "vertex"))
 +
 +
 +class EdgeDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Edge"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return Edge(reader.toObject(d["id"]),
 +                    Vertex(reader.toObject(d["outV"]), d.get("outVLabel", 
"vertex")),
 +                    d.get("label", "edge"),
 +                    Vertex(reader.toObject(d["inV"]), d.get("inVLabel", 
"vertex")))
 +
 +
 +class VertexPropertyDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:VertexProperty"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        vertex = Vertex(reader.toObject(d.get("vertex"))) if "vertex" in d 
else None
 +        return VertexProperty(reader.toObject(d["id"]),
 +                              d["label"],
 +                              reader.toObject(d["value"]),
 +                              vertex)
 +
 +
 +class PropertyDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Property"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        element = reader.toObject(d["element"]) if "element" in d else None
 +        return Property(d["key"], reader.toObject(d["value"]), element)
 +
 +
 +class PathDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Path"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return Path(reader.toObject(d["labels"]), 
reader.toObject(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.typedValue(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.toObject(d)
 +
 +
 +class MetricsDeserializer(_GraphSONTypeIO):
 +    graphson_type = "g:Metrics"
 +
 +    @classmethod
 +    def objectify(cls, d, reader):
 +        return reader.toObject(d)
diff --cc gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
index 96682e7,0000000..32d2c04
mode 100644,000000..100644
--- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
+++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py
@@@ -1,520 -1,0 +1,519 @@@
 +#
 +# Licensed to the Apache Software Foundation (ASF) under one
 +# or more contributor license agreements.  See the NOTICE file
 +# distributed with this work for additional information
 +# regarding copyright ownership.  The ASF licenses this file
 +# to you under the Apache License, Version 2.0 (the
 +# "License"); you may not use this file except in compliance
 +# with the License.  You may obtain a copy of the License at
 +# 
 +# http://www.apache.org/licenses/LICENSE-2.0
 +# 
 +# Unless required by applicable law or agreed to in writing,
 +# software distributed under the License is distributed on an
 +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +# KIND, either express or implied.  See the License for the
 +# specific language governing permissions and limitations
 +# under the License.
 +#
 +
 +__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 +
 +import datetime
 +import time
 +import json
 +import uuid
 +import math
 +from decimal import *
 +
 +from mock import Mock
 +
 +from gremlin_python.statics import *
 +from gremlin_python.structure.graph import Vertex, Edge, Property, 
VertexProperty, Graph, Path
 +from gremlin_python.structure.io.graphsonV2d0 import GraphSONWriter, 
GraphSONReader, GraphSONUtil
 +import gremlin_python.structure.io.graphsonV2d0
 +from gremlin_python.process.traversal import P
 +from gremlin_python.process.strategies import SubgraphStrategy
 +from gremlin_python.process.graph_traversal import __
 +
 +
 +class TestGraphSONReader(object):
 +    graphson_reader = GraphSONReader()
 +
 +    def test_number_input(self):
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:Byte",
 +            "@value": 1
 +        }))
 +        assert isinstance(x, SingleByte)
 +        assert 1 == x
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Int32",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, int)
 +        assert 31 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Int64",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, long)
 +        assert long(31) == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Float",
 +            "@value": 31.3
 +        }))
 +        assert isinstance(x, float)
 +        assert 31.3 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": 31.2
 +        }))
 +        assert isinstance(x, float)
 +        assert 31.2 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "NaN"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isnan(x)
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "Infinity"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isinf(x) and x > 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "-Infinity"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isinf(x) and x < 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": 31.2
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert Decimal(31.2) == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": 123456789987654321123456789987654321
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert Decimal('123456789987654321123456789987654321') == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "NaN"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isnan(x)
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "Infinity"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isinf(x) and x > 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "-Infinity"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isinf(x) and x < 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigInteger",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, long)
 +        assert 31 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigInteger",
 +            "@value": 123456789987654321123456789987654321
 +        }))
 +        assert isinstance(x, long)
 +        assert 123456789987654321123456789987654321 == x
 +
 +    def test_graph(self):
 +        vertex = self.graphson_reader.readObject("""
 +        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","outE":{"created":[{"id":{"@type":"g:Int32","@value":9},"inV":{"@type":"g:Int32","@value":3},"properties":{"weight":{"@type":"g:Double","@value":0.4}}}],"knows":[{"id":{"@type":"g:Int32","@value":7},"inV":{"@type":"g:Int32","@value":2},"properties":{"weight":{"@type":"g:Double","@value":0.5}}},{"id":{"@type":"g:Int32","@value":8},"inV":{"@type":"g:Int32","@value":4},"properties":{"weight":{"@type
 [...]
 +        assert isinstance(vertex, Vertex)
 +        assert "person" == vertex.label
 +        assert 1 == vertex.id
 +        assert isinstance(vertex.id, int)
 +        assert vertex == Vertex(1)
 +        ##
 +        vertex = self.graphson_reader.readObject("""
 +        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Float","@value":45.23}}}""")
 +        assert isinstance(vertex, Vertex)
 +        assert 45.23 == vertex.id
 +        assert isinstance(vertex.id, FloatType)
 +        assert "vertex" == vertex.label
 +        assert vertex == Vertex(45.23)
 +        ##
 +        vertex_property = self.graphson_reader.readObject("""
 +        {"@type":"g:VertexProperty", 
"@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""")
 +        assert isinstance(vertex_property, VertexProperty)
 +        assert "anId" == vertex_property.id
 +        assert "aKey" == vertex_property.label
 +        assert vertex_property.value
 +        assert vertex_property.vertex == Vertex(9)
 +        ##
 +        vertex_property = self.graphson_reader.readObject("""
 +        {"@type":"g:VertexProperty", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""")
 +        assert isinstance(vertex_property, VertexProperty)
 +        assert 1 == vertex_property.id
 +        assert "name" == vertex_property.label
 +        assert "marko" == vertex_property.value
 +        assert vertex_property.vertex is None
 +        ##
 +        edge = self.graphson_reader.readObject("""
 +        {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""")
 +        # print edge
 +        assert isinstance(edge, Edge)
 +        assert 17 == edge.id
 +        assert "knows" == edge.label
 +        assert edge.inV == Vertex("x", "xLabel")
 +        assert edge.outV == Vertex("y", "vertex")
 +        ##
 +        property = self.graphson_reader.readObject("""
 +        {"@type":"g:Property", 
"@value":{"key":"aKey","value":{"@type":"g:Int64","@value":17},"element":{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":122},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab"}}}}""")
 +        # print property
 +        assert isinstance(property, Property)
 +        assert "aKey" == property.key
 +        assert 17 == property.value
 +        assert Edge(122, Vertex("x"), "knows", Vertex("y")) == 
property.element
 +
 +    def test_path(self):
 +        path = self.graphson_reader.readObject(
 +            
"""{"@type":"g:Path","@value":{"labels":[["a"],["b","c"],[]],"objects":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":1},"value":{"@type":"g:Int32","@value":29},"label":"age"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32"
 [...]
 +        )
 +        assert isinstance(path, Path)
 +        assert "path[v[1], v[3], lop]" == str(path)
 +        assert Vertex(1) == path[0]
 +        assert Vertex(1) == path["a"]
 +        assert "lop" == path[2]
 +        assert 3 == len(path)
 +
 +    def test_custom_mapping(self):
 +
 +        # extended mapping
 +        class X(object):
 +            pass
 +
 +        type_string = "test:Xtype"
 +        override_string = "g:Int64"
 +        serdes = Mock()
 +
 +        reader = GraphSONReader(deserializer_map={type_string: serdes})
 +        assert type_string in reader.deserializers
 +
 +        # base dicts are not modified
 +        assert type_string not in 
gremlin_python.structure.io.graphsonV2d0._deserializers
 +
 +        x = X()
 +        o = reader.toObject({GraphSONUtil.TYPE_KEY: type_string, 
GraphSONUtil.VALUE_KEY: x})
 +        serdes.objectify.assert_called_once_with(x, reader)
 +        assert o is serdes.objectify()
 +
 +        # overridden mapping
 +        type_string = "g:Int64"
 +        serdes = Mock()
 +        reader = GraphSONReader(deserializer_map={type_string: serdes, 
override_string: serdes})
 +        assert 
gremlin_python.structure.io.graphsonV2d0._deserializers[type_string] is not 
reader.deserializers[type_string]
 +
 +        value = 3
 +        o = reader.toObject({GraphSONUtil.TYPE_KEY: type_string, 
GraphSONUtil.VALUE_KEY: value})
 +        serdes.objectify.assert_called_once_with(value, reader)
 +        assert o is serdes.objectify()
 +
 +    def test_datetime(self):
 +        expected = datetime.datetime(2016, 12, 14, 16, 14, 36, 295000)
 +        pts = time.mktime(expected.timetuple()) + expected.microsecond / 1e6 
- \
 +                         (time.mktime(datetime.datetime(1970, 1, 
1).timetuple()))
 +        ts = int(round(pts * 1000))
 +        dt = self.graphson_reader.readObject(json.dumps({"@type": "g:Date", 
"@value": ts}))
 +        assert isinstance(dt, datetime.datetime)
 +        # TINKERPOP-1848
 +        assert dt == expected
 +
 +    def test_timestamp(self):
 +        dt = self.graphson_reader.readObject(json.dumps({"@type": 
"g:Timestamp", "@value": 1481750076295}))
 +        assert isinstance(dt, timestamp)
 +        assert float(dt) == 1481750076.295
 +
 +    def test_duration(self):
 +        d = self.graphson_reader.readObject(json.dumps({"@type": 
"gx:Duration", "@value": "PT120H"}))
 +        assert isinstance(d, datetime.timedelta)
 +        assert d == datetime.timedelta(hours=120)
 +
 +    def test_uuid(self):
 +        prop = self.graphson_reader.readObject(
 +            json.dumps({'@type': 'g:UUID', '@value': 
"41d2e28a-20a4-4ab0-b379-d810dede3786"}))
 +        assert isinstance(prop, uuid.UUID)
 +        assert str(prop) == '41d2e28a-20a4-4ab0-b379-d810dede3786'
 +
 +    def test_metrics(self):
 +        prop = self.graphson_reader.readObject(
 +            json.dumps([{'@type': 'g:TraversalMetrics', '@value': {'dur': 
1.468594, 'metrics': [
 +                {'@type': 'g:Metrics', '@value': {'dur': 1.380957, 'counts': 
{}, 'name': 'GraphStep(__.V())', 'annotations': {'percentDur': 
94.03259171697556}, 'id': '4.0.0()'}},
 +                {'@type': 'g:Metrics', '@value': {'dur': 0.087637, 'counts': 
{}, 'name': 'ReferenceElementStep', 'annotations': {'percentDur': 
5.967408283024444}, 'id': '3.0.0()'}}
 +            ]}}]))
 +        assert isinstance(prop, list)
 +        assert prop == [{'dur': 1.468594, 'metrics': [
 +                {'dur': 1.380957, 'counts': {}, 'name': 'GraphStep(__.V())', 
'annotations': {'percentDur': 94.03259171697556}, 'id': '4.0.0()'},
 +                {'dur': 0.087637, 'counts': {}, 'name': 
'ReferenceElementStep', 'annotations': {'percentDur': 5.967408283024444}, 'id': 
'3.0.0()'}
 +                ]}]
 +
 +    def test_bytebuffer(self):
 +        bb = self.graphson_reader.readObject(
 +            json.dumps({"@type": "gx:ByteBuffer", "@value": 
"c29tZSBieXRlcyBmb3IgeW91"}))
 +        assert isinstance(bb, ByteBufferType)
 +        assert ByteBufferType("c29tZSBieXRlcyBmb3IgeW91", "utf8") == bb
 +
 +    def test_char(self):
 +        c = self.graphson_reader.readObject(json.dumps({"@type": "gx:Char", 
"@value": "L"}))
 +        assert isinstance(c, SingleChar)
 +        assert chr(76) == c
 +
 +    def test_null(self):
 +        c = self.graphson_reader.readObject(json.dumps(None))
 +        assert c is None
 +
 +
 +class TestGraphSONWriter(object):
 +    graphson_writer = GraphSONWriter()
 +    graphson_reader = GraphSONReader()
 +
-     def test_number_output(self):
-         assert {"@type": "g:Int64", "@value": 2} == 
json.loads(self.graphson_writer.writeObject(long(2)))
-         assert {"@type": "g:Int32", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(1))
-         assert {"@type": "g:Double", "@value": 3.2} == 
json.loads(self.graphson_writer.writeObject(3.2))
-         assert """true""" == self.graphson_writer.writeObject(True)
- 
 +    def test_numbers(self):
 +        assert {"@type": "gx:Byte", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(int.__new__(SingleByte, 1)))
 +        assert {"@type": "g:Int64", "@value": 2} == 
json.loads(self.graphson_writer.writeObject(long(2)))
++        assert {"@type": "g:Int64", "@value": 851401972585122} == 
json.loads(self.graphson_writer.writeObject(long(851401972585122)))
++        assert {"@type": "g:Int64", "@value": -2} == 
json.loads(self.graphson_writer.writeObject(long(-2)))
++        assert {"@type": "g:Int64", "@value": -851401972585122} == 
json.loads(self.graphson_writer.writeObject(long(-851401972585122)))
 +        assert {"@type": "g:Int32", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(1))
++        assert {"@type": "g:Int32", "@value": -1} == 
json.loads(self.graphson_writer.writeObject(-1))
++        assert {"@type": "g:Int64", "@value": 851401972585122} == 
json.loads(self.graphson_writer.writeObject(851401972585122))
 +        assert {"@type": "g:Double", "@value": 3.2} == 
json.loads(self.graphson_writer.writeObject(3.2))
 +        assert {"@type": "g:Double", "@value": "NaN"} == 
json.loads(self.graphson_writer.writeObject(float('nan')))
 +        assert {"@type": "g:Double", "@value": "Infinity"} == 
json.loads(self.graphson_writer.writeObject(float('inf')))
 +        assert {"@type": "g:Double", "@value": "-Infinity"} == 
json.loads(self.graphson_writer.writeObject(float('-inf')))
 +        assert {"@type": "gx:BigDecimal", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(Decimal('123456789987654321123456789987654321')))
 +        assert {"@type": "gx:BigDecimal", "@value": "NaN"} == 
json.loads(self.graphson_writer.writeObject(Decimal('nan')))
 +        assert {"@type": "gx:BigDecimal", "@value": "Infinity"} == 
json.loads(self.graphson_writer.writeObject(Decimal('inf')))
 +        assert {"@type": "gx:BigDecimal", "@value": "-Infinity"} == 
json.loads(self.graphson_writer.writeObject(Decimal('-inf')))
 +        assert {"@type": "gx:BigInteger", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(long(123456789987654321123456789987654321)))
 +        assert {"@type": "gx:BigInteger", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(123456789987654321123456789987654321))
 +        assert """true""" == self.graphson_writer.writeObject(True)
 +
 +    def test_P(self):
 +        result = {'@type': 'g:P',
 +                  '@value': {
 +                      'predicate': 'and',
 +                      'value': [{
 +                          '@type': 'g:P',
 +                          '@value': {
 +                              'predicate': 'or',
 +                              'value': [{
 +                                  '@type': 'g:P',
 +                                  '@value': {'predicate': 'lt', 'value': 'b'}
 +                              },
 +                                  {'@type': 'g:P', '@value': {'predicate': 
'gt', 'value': 'c'}}
 +                              ]
 +                          }
 +                      },
 +                          {'@type': 'g:P', '@value': {'predicate': 'neq', 
'value': 'd'}}]}}
 +
 +        assert result == json.loads(
 +            
self.graphson_writer.writeObject(P.lt("b").or_(P.gt("c")).and_(P.neq("d"))))
 +
 +        result = {'@type': 'g:P', '@value': {'predicate':'within','value': 
[{"@type": "g:Int32", "@value": 1},{"@type": "g:Int32", "@value": 2}]}}
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within([1, 2])))
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within(1, 2)))
 +
 +        result = {'@type': 'g:P', '@value': {'predicate':'within','value': 
[{"@type": "g:Int32", "@value": 1}]}}
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within([1])))
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within(1)))
 +
 +    def test_strategies(self):
 +        # we have a proxy model for now given that we don't want to have to 
have g:XXX all registered on the Gremlin traversal machine (yet)
 +        assert {"@type": "g:SubgraphStrategy", "@value": {}} == json.loads(
 +            self.graphson_writer.writeObject(SubgraphStrategy))
 +        assert {"@type": "g:SubgraphStrategy", "@value": {
 +            "vertices": {"@type": "g:Bytecode", "@value": {"step": [["has", 
"name", "marko"]]}}}} == json.loads(
 +            
self.graphson_writer.writeObject(SubgraphStrategy(vertices=__.has("name", 
"marko"))))
 +
 +    def test_graph(self):
 +        # TODO: this assert is not compatible with python 3 and now that we 
test with both 2 and 3 it fails
 +        assert {"@type": "g:Vertex", "@value": {"id": {"@type": "g:Int64", 
"@value": 12}, "label": "person"}} == 
json.loads(self.graphson_writer.writeObject(Vertex(long(12), "person")))
 +
 +        assert {"@type": "g:Edge", "@value": {"id": {"@type": "g:Int32", 
"@value": 7},
 +                                              "outV": {"@type": "g:Int32", 
"@value": 0},
 +                                              "outVLabel": "person",
 +                                              "label": "knows",
 +                                              "inV": {"@type": "g:Int32", 
"@value": 1},
 +                                              "inVLabel": "dog"}} == 
json.loads(
 +            self.graphson_writer.writeObject(Edge(7, Vertex(0, "person"), 
"knows", Vertex(1, "dog"))))
 +        assert {"@type": "g:VertexProperty", "@value": {"id": "blah", 
"label": "keyA", "value": True,
 +                                                        "vertex": "stephen"}} 
== json.loads(
 +            self.graphson_writer.writeObject(VertexProperty("blah", "keyA", 
True, Vertex("stephen"))))
 +
 +        assert {"@type": "g:Property",
 +                "@value": {"key": "name", "value": "marko", "element": 
{"@type": "g:VertexProperty",
 +                                                                        
"@value": {
 +                                                                            
"vertex": "vertexId",
 +                                                                            
"id": {"@type": "g:Int32", "@value": 1234},
 +                                                                            
"label": "aKey"}}}} == json.loads(
 +            self.graphson_writer.writeObject(
 +                Property("name", "marko", VertexProperty(1234, "aKey", 21345, 
Vertex("vertexId")))))
 +
 +        vertex = 
self.graphson_reader.readObject(self.graphson_writer.writeObject(Vertex(1, 
"person")))
 +        assert 1 == vertex.id
 +        assert "person" == vertex.label
 +
 +        edge = self.graphson_reader.readObject(
 +            self.graphson_writer.writeObject(Edge(3, Vertex(1, "person"), 
"knows", Vertex(2, "dog"))))
 +        assert "knows" == edge.label
 +        assert 3 == edge.id
 +        assert 1 == edge.outV.id
 +        assert 2 == edge.inV.id
 +
 +        vertex_property = self.graphson_reader.readObject(
 +            self.graphson_writer.writeObject(VertexProperty(1, "age", 32, 
Vertex(1))))
 +        assert 1 == vertex_property.id
 +        assert "age" == vertex_property.key
 +        assert 32 == vertex_property.value
 +
 +        property = 
self.graphson_reader.readObject(self.graphson_writer.writeObject(Property("age",
 32.2, Edge(1,Vertex(2),"knows",Vertex(3)))))
 +        assert "age" == property.key
 +        assert 32.2 == property.value
 +
 +    def test_custom_mapping(self):
 +        # extended mapping
 +        class X(object):
 +            pass
 +
 +        serdes = Mock()
 +        writer = GraphSONWriter(serializer_map={X: serdes})
 +        assert X in writer.serializers
 +
 +        # base dicts are not modified
 +        assert X not in gremlin_python.structure.io.graphsonV2d0._serializers
 +
 +        obj = X()
 +        d = writer.toDict(obj)
 +        serdes.dictify.assert_called_once_with(obj, writer)
 +        assert d is serdes.dictify()
 +
 +        # overridden mapping
 +        serdes = Mock()
 +        writer = GraphSONWriter(serializer_map={int: serdes})
 +        assert gremlin_python.structure.io.graphsonV2d0._serializers[int] is 
not writer.serializers[int]
 +
 +        value = 3
 +        d = writer.toDict(value)
 +        serdes.dictify.assert_called_once_with(value, writer)
 +        assert d is serdes.dictify()
 +
 +    def test_write_long(self):
 +        mapping = self.graphson_writer.toDict(1)
 +        assert mapping['@type'] == 'g:Int32'
 +        assert mapping['@value'] == 1
 +
 +        mapping = self.graphson_writer.toDict(long(1))
 +        assert mapping['@type'] == 'g:Int64'
 +        assert mapping['@value'] == 1
 +
 +    def test_datetime(self):
 +        expected = json.dumps({"@type": "g:Date", "@value": 1481750076295}, 
separators=(',', ':'))
 +        dt = datetime.datetime.utcfromtimestamp(1481750076295 / 1000.0)
 +        output = self.graphson_writer.writeObject(dt)
 +        assert expected == output
 +
 +    def test_timestamp(self):
 +        expected = json.dumps({"@type": "g:Timestamp", "@value": 
1481750076295}, separators=(',', ':'))
 +        ts = timestamp(1481750076295 / 1000.0)
 +        output = self.graphson_writer.writeObject(ts)
 +        assert expected == output
 +
 +    def test_duration(self):
 +        expected = json.dumps({"@type": "gx:Duration", "@value": "P5D"}, 
separators=(',', ':'))
 +        d = datetime.timedelta(hours=120)
 +        output = self.graphson_writer.writeObject(d)
 +        assert expected == output
 +
 +    def test_uuid(self):
 +        expected = json.dumps({'@type': 'g:UUID', '@value': 
"41d2e28a-20a4-4ab0-b379-d810dede3786"}, separators=(',', ':'))
 +        prop = uuid.UUID("41d2e28a-20a4-4ab0-b379-d810dede3786")
 +        output = self.graphson_writer.writeObject(prop)
 +        assert expected == output
 +
 +    def test_bytebuffer(self):
 +        expected = json.dumps({'@type': 'gx:ByteBuffer', '@value': 
'c29tZSBieXRlcyBmb3IgeW91'}, separators=(',', ':'))
 +        bb = ByteBufferType("c29tZSBieXRlcyBmb3IgeW91", "utf8")
 +        output = self.graphson_writer.writeObject(bb)
 +        assert expected == output
 +
 +    def test_char(self):
 +        expected = json.dumps({'@type': 'gx:Char', '@value': 'L'}, 
separators=(',', ':'))
 +        c = str.__new__(SingleChar, chr(76))
 +        output = self.graphson_writer.writeObject(c)
 +        assert expected == output
 +
 +
 +class TestFunctionalGraphSONIO(object):
 +    """Functional IO tests"""
 +
 +    def test_timestamp(self, remote_connection_v2):
 +        g = Graph().traversal().withRemote(remote_connection_v2)
 +        ts = timestamp(1481750076295 / 1000)
 +        resp = g.addV('test_vertex').property('ts', ts)
 +        resp = resp.toList()
 +        vid = resp[0].id
 +        try:
 +            ts_prop = g.V(vid).properties('ts').toList()[0]
 +            assert isinstance(ts_prop.value, timestamp)
 +            assert ts_prop.value == ts
 +        except OSError:
 +            assert False, "Error making request"
 +        finally:
 +            g.V(vid).drop().iterate()
 +
 +    def test_datetime(self, remote_connection_v2):
 +        g = Graph().traversal().withRemote(remote_connection_v2)
 +        dt = datetime.datetime.utcfromtimestamp(1481750076295 / 1000)
 +        resp = g.addV('test_vertex').property('dt', dt).toList()
 +        vid = resp[0].id
 +        try:
 +            dt_prop = g.V(vid).properties('dt').toList()[0]
 +            assert isinstance(dt_prop.value, datetime.datetime)
 +            assert dt_prop.value == dt
 +        except OSError:
 +            assert False, "Error making request"
 +        finally:
 +            g.V(vid).drop().iterate()
 +
 +    def test_uuid(self, remote_connection_v2):
 +        g = Graph().traversal().withRemote(remote_connection_v2)
 +        uid = uuid.UUID("41d2e28a-20a4-4ab0-b379-d810dede3786")
 +        resp = g.addV('test_vertex').property('uuid', uid).toList()
 +        vid = resp[0].id
 +        try:
 +            uid_prop = g.V(vid).properties('uuid').toList()[0]
 +            assert isinstance(uid_prop.value, uuid.UUID)
 +            assert uid_prop.value == uid
 +        except OSError:
 +            assert False, "Error making request"
 +        finally:
 +            g.V(vid).drop().iterate()
diff --cc gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
index 1644a1a,0000000..d7b837b
mode 100644,000000..100644
--- a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
+++ b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py
@@@ -1,538 -1,0 +1,537 @@@
 +#
 +# Licensed to the Apache Software Foundation (ASF) under one
 +# or more contributor license agreements.  See the NOTICE file
 +# distributed with this work for additional information
 +# regarding copyright ownership.  The ASF licenses this file
 +# to you under the Apache License, Version 2.0 (the
 +# "License"); you may not use this file except in compliance
 +# with the License.  You may obtain a copy of the License at
 +# 
 +# http://www.apache.org/licenses/LICENSE-2.0
 +# 
 +# Unless required by applicable law or agreed to in writing,
 +# software distributed under the License is distributed on an
 +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 +# KIND, either express or implied.  See the License for the
 +# specific language governing permissions and limitations
 +# under the License.
 +#
 +
 +__author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 +
 +import datetime
 +import time
 +import json
 +import uuid
 +import math
 +from decimal import *
 +
 +from mock import Mock
 +
 +from gremlin_python.statics import *
 +from gremlin_python.structure.graph import Vertex, Edge, Property, 
VertexProperty, Graph, Path
 +from gremlin_python.structure.io.graphsonV3d0 import GraphSONWriter, 
GraphSONReader, GraphSONUtil
 +import gremlin_python.structure.io.graphsonV3d0
 +from gremlin_python.process.traversal import P
 +from gremlin_python.process.strategies import SubgraphStrategy
 +from gremlin_python.process.graph_traversal import __
 +
 +
 +class TestGraphSONReader(object):
 +    graphson_reader = GraphSONReader()
 +
 +    def test_collections(self):
 +        x = self.graphson_reader.readObject(
 +            json.dumps({"@type": "g:List", "@value": [{"@type": "g:Int32", 
"@value": 1},
 +                                                      {"@type": "g:Int32", 
"@value": 2},
 +                                                      "3"]}))
 +        assert isinstance(x, list)
 +        assert x[0] == 1
 +        assert x[1] == 2
 +        assert x[2] == "3"
 +        ##
 +
 +        x = self.graphson_reader.readObject(
 +            json.dumps({"@type": "g:Set", "@value": [{"@type": "g:Int32", 
"@value": 1},
 +                                                     {"@type": "g:Int32", 
"@value": 2},
 +                                                     "3"]}))
 +        # return a set as normal
 +        assert isinstance(x, set)
 +        assert x == set([1, 2, "3"])
 +
 +        x = self.graphson_reader.readObject(
 +            json.dumps({"@type": "g:Set", "@value": [{"@type": "g:Int32", 
"@value": 1},
 +                                                    {"@type": "g:Int32", 
"@value": 2},
 +                                                    {"@type": "g:Float", 
"@value": 2.0},
 +                                                    "3"]}))
 +        # coerce to list here because Java might return numerics of different 
types which python won't recognize
 +        # see comments of TINKERPOP-1844 for more details
 +        assert isinstance(x, list)
 +        assert x == list([1, 2, 2.0, "3"])
 +        ##
 +        x = self.graphson_reader.readObject(
 +            json.dumps({"@type": "g:Map",
 +                        "@value": ['a', {"@type": "g:Int32", "@value": 1}, 
'b', "marko"]}))
 +        assert isinstance(x, dict)
 +        assert x['a'] == 1
 +        assert x['b'] == "marko"
 +        assert len(x) == 2
 +
 +        # BulkSet gets coerced to a List - both have the same behavior
 +        x = self.graphson_reader.readObject(
 +            json.dumps({"@type": "g:BulkSet",
 +                        "@value": ["marko", {"@type": "g:Int64", "@value": 
1}, "josh", {"@type": "g:Int64", "@value": 3}]}))
 +        assert isinstance(x, list)
 +        assert len(x) == 4
 +        assert x.count("marko") == 1
 +        assert x.count("josh") == 3
 +
 +    def test_number_input(self):
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:Byte",
 +            "@value": 1
 +        }))
 +        assert isinstance(x, SingleByte)
 +        assert 1 == x
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Int32",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, int)
 +        assert 31 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Int64",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, long)
 +        assert long(31) == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Float",
 +            "@value": 31.3
 +        }))
 +        assert isinstance(x, float)
 +        assert 31.3 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": 31.2
 +        }))
 +        assert isinstance(x, float)
 +        assert 31.2 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "NaN"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isnan(x)
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "Infinity"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isinf(x) and x > 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "g:Double",
 +            "@value": "-Infinity"
 +        }))
 +        assert isinstance(x, float)
 +        assert math.isinf(x) and x < 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": 31.2
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert Decimal(31.2) == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": 123456789987654321123456789987654321
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert Decimal('123456789987654321123456789987654321') == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "NaN"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isnan(x)
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "Infinity"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isinf(x) and x > 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigDecimal",
 +            "@value": "-Infinity"
 +        }))
 +        assert isinstance(x, Decimal)
 +        assert math.isinf(x) and x < 0
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigInteger",
 +            "@value": 31
 +        }))
 +        assert isinstance(x, long)
 +        assert 31 == x
 +        ##
 +        x = self.graphson_reader.readObject(json.dumps({
 +            "@type": "gx:BigInteger",
 +            "@value": 123456789987654321123456789987654321
 +        }))
 +        assert isinstance(x, long)
 +        assert 123456789987654321123456789987654321 == x
 +
 +    def test_graph(self):
 +        vertex = self.graphson_reader.readObject("""
 +        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","outE":{"created":[{"id":{"@type":"g:Int32","@value":9},"inV":{"@type":"g:Int32","@value":3},"properties":{"weight":{"@type":"g:Double","@value":0.4}}}],"knows":[{"id":{"@type":"g:Int32","@value":7},"inV":{"@type":"g:Int32","@value":2},"properties":{"weight":{"@type":"g:Double","@value":0.5}}},{"id":{"@type":"g:Int32","@value":8},"inV":{"@type":"g:Int32","@value":4},"properties":{"weight":{"@type
 [...]
 +        assert isinstance(vertex, Vertex)
 +        assert "person" == vertex.label
 +        assert 1 == vertex.id
 +        assert isinstance(vertex.id, int)
 +        assert vertex == Vertex(1)
 +        ##
 +        vertex = self.graphson_reader.readObject("""
 +        {"@type":"g:Vertex", 
"@value":{"id":{"@type":"g:Float","@value":45.23}}}""")
 +        assert isinstance(vertex, Vertex)
 +        assert 45.23 == vertex.id
 +        assert isinstance(vertex.id, FloatType)
 +        assert "vertex" == vertex.label
 +        assert vertex == Vertex(45.23)
 +        ##
 +        vertex_property = self.graphson_reader.readObject("""
 +        {"@type":"g:VertexProperty", 
"@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""")
 +        assert isinstance(vertex_property, VertexProperty)
 +        assert "anId" == vertex_property.id
 +        assert "aKey" == vertex_property.label
 +        assert vertex_property.value
 +        assert vertex_property.vertex == Vertex(9)
 +        ##
 +        vertex_property = self.graphson_reader.readObject("""
 +        {"@type":"g:VertexProperty", 
"@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""")
 +        assert isinstance(vertex_property, VertexProperty)
 +        assert 1 == vertex_property.id
 +        assert "name" == vertex_property.label
 +        assert "marko" == vertex_property.value
 +        assert vertex_property.vertex is None
 +        ##
 +        edge = self.graphson_reader.readObject("""
 +        {"@type":"g:Edge", 
"@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""")
 +        # print edge
 +        assert isinstance(edge, Edge)
 +        assert 17 == edge.id
 +        assert "knows" == edge.label
 +        assert edge.inV == Vertex("x", "xLabel")
 +        assert edge.outV == Vertex("y", "vertex")
 +        ##
 +        property = self.graphson_reader.readObject("""
 +        {"@type":"g:Property", 
"@value":{"key":"aKey","value":{"@type":"g:Int64","@value":17},"element":{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":122},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab"}}}}""")
 +        # print property
 +        assert isinstance(property, Property)
 +        assert "aKey" == property.key
 +        assert 17 == property.value
 +        assert Edge(122, Vertex("x"), "knows", Vertex("y")) == 
property.element
 +
 +    def test_path(self):
 +        path = self.graphson_reader.readObject(
 +            
"""{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["a"]},{"@type":"g:Set","@value":["b","c"]},{"@type":"g:Set","@value":[]}]},"objects":{"@type":"g:List","@value":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":1},"label":"person","properties":{"name":[{"@type":"g:VertexProperty","@value":{"id":{"@type":"g:Int64","@value":0},"value":"marko","label":"name"}}],"age":[{"@type":"g:VertexProperty","@value":{"id":{"@type"
 [...]
 +        )
 +        assert isinstance(path, Path)
 +        assert "path[v[1], v[3], lop]" == str(path)
 +        assert Vertex(1) == path[0]
 +        assert Vertex(1) == path["a"]
 +        assert "lop" == path[2]
 +        assert 3 == len(path)
 +
 +    def test_custom_mapping(self):
 +
 +        # extended mapping
 +        class X(object):
 +            pass
 +
 +        type_string = "test:Xtype"
 +        override_string = "g:Int64"
 +        serdes = Mock()
 +
 +        reader = GraphSONReader(deserializer_map={type_string: serdes})
 +        assert type_string in reader.deserializers
 +
 +        # base dicts are not modified
 +        assert type_string not in 
gremlin_python.structure.io.graphsonV3d0._deserializers
 +
 +        x = X()
 +        o = reader.toObject({GraphSONUtil.TYPE_KEY: type_string, 
GraphSONUtil.VALUE_KEY: x})
 +        serdes.objectify.assert_called_once_with(x, reader)
 +        assert o is serdes.objectify()
 +
 +        # overridden mapping
 +        type_string = "g:Int64"
 +        serdes = Mock()
 +        reader = GraphSONReader(deserializer_map={type_string: serdes, 
override_string: serdes})
 +        assert 
gremlin_python.structure.io.graphsonV3d0._deserializers[type_string] is not 
reader.deserializers[
 +            type_string]
 +
 +        value = 3
 +        o = reader.toObject({GraphSONUtil.TYPE_KEY: type_string, 
GraphSONUtil.VALUE_KEY: value})
 +        serdes.objectify.assert_called_once_with(value, reader)
 +        assert o is serdes.objectify()
 +
 +    def test_datetime(self):
 +        expected = datetime.datetime(2016, 12, 14, 16, 14, 36, 295000)
 +        pts = time.mktime(expected.timetuple()) + expected.microsecond / 1e6 
- \
 +              (time.mktime(datetime.datetime(1970, 1, 1).timetuple()))
 +        ts = int(round(pts * 1000))
 +        dt = self.graphson_reader.readObject(json.dumps({"@type": "g:Date", 
"@value": ts}))
 +        assert isinstance(dt, datetime.datetime)
 +        # TINKERPOP-1848
 +        assert dt == expected
 +
 +    def test_timestamp(self):
 +        dt = self.graphson_reader.readObject(json.dumps({"@type": 
"g:Timestamp", "@value": 1481750076295}))
 +        assert isinstance(dt, timestamp)
 +        assert float(dt) == 1481750076.295
 +
 +    def test_duration(self):
 +        d = self.graphson_reader.readObject(json.dumps({"@type": 
"gx:Duration", "@value": "PT120H"}))
 +        assert isinstance(d, datetime.timedelta)
 +        assert d == datetime.timedelta(hours=120)
 +
 +    def test_uuid(self):
 +        prop = self.graphson_reader.readObject(
 +            json.dumps({'@type': 'g:UUID', '@value': 
"41d2e28a-20a4-4ab0-b379-d810dede3786"}))
 +        assert isinstance(prop, uuid.UUID)
 +        assert str(prop) == '41d2e28a-20a4-4ab0-b379-d810dede3786'
 +
 +    def test_metrics(self):
 +        prop = self.graphson_reader.readObject(
 +            json.dumps([{'@type': 'g:TraversalMetrics', '@value': {'dur': 
1.468594, 'metrics': [
 +                {'@type': 'g:Metrics', '@value': {'dur': 1.380957, 'counts': 
{}, 'name': 'GraphStep(__.V())', 'annotations': {'percentDur': 
94.03259171697556}, 'id': '4.0.0()'}},
 +                {'@type': 'g:Metrics', '@value': {'dur': 0.087637, 'counts': 
{}, 'name': 'ReferenceElementStep', 'annotations': {'percentDur': 
5.967408283024444}, 'id': '3.0.0()'}}
 +            ]}}]))
 +        assert isinstance(prop, list)
 +        assert prop == [{'dur': 1.468594, 'metrics': [
 +                {'dur': 1.380957, 'counts': {}, 'name': 'GraphStep(__.V())', 
'annotations': {'percentDur': 94.03259171697556}, 'id': '4.0.0()'},
 +                {'dur': 0.087637, 'counts': {}, 'name': 
'ReferenceElementStep', 'annotations': {'percentDur': 5.967408283024444}, 'id': 
'3.0.0()'}
 +                ]}]
 +
 +    def test_bytebuffer(self):
 +        bb = self.graphson_reader.readObject(
 +            json.dumps({"@type": "gx:ByteBuffer", "@value": 
"c29tZSBieXRlcyBmb3IgeW91"}))
 +        assert isinstance(bb, ByteBufferType)
 +        assert ByteBufferType("c29tZSBieXRlcyBmb3IgeW91", "utf8") == bb
 +
 +    def test_char(self):
 +        c = self.graphson_reader.readObject(json.dumps({"@type": "gx:Char", 
"@value": "L"}))
 +        assert isinstance(c, SingleChar)
 +        assert chr(76) == c
 +
 +    def test_null(self):
 +        c = self.graphson_reader.readObject(json.dumps(None))
 +        assert c is None
 +
 +
 +class TestGraphSONWriter(object):
 +    graphson_writer = GraphSONWriter()
 +    graphson_reader = GraphSONReader()
 +
 +    def test_collections(self):
 +        assert {"@type": "g:List", "@value": [{"@type": "g:Int32", "@value": 
1},
 +                                              {"@type": "g:Int32", "@value": 
2},
 +                                              {"@type": "g:Int32", "@value": 
3}]} == json.loads(
 +            self.graphson_writer.writeObject([1, 2, 3]))
 +        assert {"@type": "g:Set", "@value": [{"@type": "g:Int32", "@value": 
1},
 +                                             {"@type": "g:Int32", "@value": 
2},
 +                                             {"@type": "g:Int32", "@value": 
3}]} == json.loads(
 +            self.graphson_writer.writeObject(set([1, 2, 3, 3])))
 +        assert {"@type": "g:Map",
 +                "@value": ['a', {"@type": "g:Int32", "@value": 1}]} == 
json.loads(
 +            self.graphson_writer.writeObject({'a': 1}))
 +
-     def test_number_output(self):
-         assert {"@type": "g:Int64", "@value": 2} == 
json.loads(self.graphson_writer.writeObject(long(2)))
-         assert {"@type": "g:Int32", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(1))
-         assert {"@type": "g:Double", "@value": 3.2} == 
json.loads(self.graphson_writer.writeObject(3.2))
-         assert """true""" == self.graphson_writer.writeObject(True)
- 
 +    def test_numbers(self):
 +        assert {"@type": "gx:Byte", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(int.__new__(SingleByte, 1)))
 +        assert {"@type": "g:Int64", "@value": 2} == 
json.loads(self.graphson_writer.writeObject(long(2)))
++        assert {"@type": "g:Int64", "@value": 851401972585122} == 
json.loads(self.graphson_writer.writeObject(long(851401972585122)))
++        assert {"@type": "g:Int64", "@value": -2} == 
json.loads(self.graphson_writer.writeObject(long(-2)))
++        assert {"@type": "g:Int64", "@value": -851401972585122} == 
json.loads(self.graphson_writer.writeObject(long(-851401972585122)))
 +        assert {"@type": "g:Int32", "@value": 1} == 
json.loads(self.graphson_writer.writeObject(1))
++        assert {"@type": "g:Int32", "@value": -1} == 
json.loads(self.graphson_writer.writeObject(-1))
++        assert {"@type": "g:Int64", "@value": 851401972585122} == 
json.loads(self.graphson_writer.writeObject(851401972585122))
 +        assert {"@type": "g:Double", "@value": 3.2} == 
json.loads(self.graphson_writer.writeObject(3.2))
 +        assert {"@type": "g:Double", "@value": "NaN"} == 
json.loads(self.graphson_writer.writeObject(float('nan')))
 +        assert {"@type": "g:Double", "@value": "Infinity"} == 
json.loads(self.graphson_writer.writeObject(float('inf')))
 +        assert {"@type": "g:Double", "@value": "-Infinity"} == 
json.loads(self.graphson_writer.writeObject(float('-inf')))
 +        assert {"@type": "gx:BigDecimal", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(Decimal('123456789987654321123456789987654321')))
 +        assert {"@type": "gx:BigDecimal", "@value": "NaN"} == 
json.loads(self.graphson_writer.writeObject(Decimal('nan')))
 +        assert {"@type": "gx:BigDecimal", "@value": "Infinity"} == 
json.loads(self.graphson_writer.writeObject(Decimal('inf')))
 +        assert {"@type": "gx:BigDecimal", "@value": "-Infinity"} == 
json.loads(self.graphson_writer.writeObject(Decimal('-inf')))
 +        assert {"@type": "gx:BigInteger", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(long(123456789987654321123456789987654321)))
 +        assert {"@type": "gx:BigInteger", "@value": 
"123456789987654321123456789987654321"} == 
json.loads(self.graphson_writer.writeObject(123456789987654321123456789987654321))
 +        assert """true""" == self.graphson_writer.writeObject(True)
 +
 +    def test_P(self):
 +        result = {'@type': 'g:P',
 +                  '@value': {
 +                      'predicate': 'and',
 +                      'value': [{
 +                          '@type': 'g:P',
 +                          '@value': {
 +                              'predicate': 'or',
 +                              'value': [{
 +                                  '@type': 'g:P',
 +                                  '@value': {'predicate': 'lt', 'value': 'b'}
 +                              },
 +                                  {'@type': 'g:P', '@value': {'predicate': 
'gt', 'value': 'c'}}
 +                              ]
 +                          }
 +                      },
 +                          {'@type': 'g:P', '@value': {'predicate': 'neq', 
'value': 'd'}}]}}
 +
 +        assert result == json.loads(
 +            
self.graphson_writer.writeObject(P.lt("b").or_(P.gt("c")).and_(P.neq("d"))))
 +
 +        result = {'@type': 'g:P', '@value': {'predicate': 'within', 'value': 
{'@type': 'g:List', '@value': [
 +            {"@type": "g:Int32", "@value": 1}, {"@type": "g:Int32", "@value": 
2}]}}}
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within([1, 2])))
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within(1, 2)))
 +
 +        result = {'@type': 'g:P', '@value': {'predicate': 'within', 'value': 
{'@type': 'g:List', '@value': [
 +            {"@type": "g:Int32", "@value": 1}]}}}
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within([1])))
 +        assert result == 
json.loads(self.graphson_writer.writeObject(P.within(1)))
 +
 +    def test_strategies(self):
 +        # we have a proxy model for now given that we don't want to have to 
have g:XXX all registered on the 
 +        # Gremlin traversal machine (yet)
 +        assert {"@type": "g:SubgraphStrategy", "@value": {}} == json.loads(
 +            self.graphson_writer.writeObject(SubgraphStrategy))
 +        assert {"@type": "g:SubgraphStrategy", "@value": {
 +            "vertices": {"@type": "g:Bytecode", "@value": {"step": [["has", 
"name", "marko"]]}}}} == json.loads(
 +            
self.graphson_writer.writeObject(SubgraphStrategy(vertices=__.has("name", 
"marko"))))
 +
 +    def test_graph(self):
 +        # TODO: this assert is not compatible with python 3 and now that we 
test with both 2 and 3 it fails
 +        assert {"@type": "g:Vertex",
 +                "@value": {"id": {"@type": "g:Int64", "@value": 12}, "label": 
"person"}} == json.loads(
 +            self.graphson_writer.writeObject(Vertex(long(12), "person")))
 +
 +        assert {"@type": "g:Edge", "@value": {"id": {"@type": "g:Int32", 
"@value": 7},
 +                                              "outV": {"@type": "g:Int32", 
"@value": 0},
 +                                              "outVLabel": "person",
 +                                              "label": "knows",
 +                                              "inV": {"@type": "g:Int32", 
"@value": 1},
 +                                              "inVLabel": "dog"}} == 
json.loads(
 +            self.graphson_writer.writeObject(Edge(7, Vertex(0, "person"), 
"knows", Vertex(1, "dog"))))
 +        assert {"@type": "g:VertexProperty", "@value": {"id": "blah", 
"label": "keyA", "value": True,
 +                                                        "vertex": "stephen"}} 
== json.loads(
 +            self.graphson_writer.writeObject(VertexProperty("blah", "keyA", 
True, Vertex("stephen"))))
 +
 +        assert {"@type": "g:Property",
 +                "@value": {"key": "name", "value": "marko", "element": 
{"@type": "g:VertexProperty",
 +                                                                        
"@value": {
 +                                                                            
"vertex": "vertexId",
 +                                                                            
"id": {"@type": "g:Int32", "@value": 1234},
 +                                                                            
"label": "aKey"}}}} == json.loads(
 +            self.graphson_writer.writeObject(
 +                Property("name", "marko", VertexProperty(1234, "aKey", 21345, 
Vertex("vertexId")))))
 +
 +        vertex = 
self.graphson_reader.readObject(self.graphson_writer.writeObject(Vertex(1, 
"person")))
 +        assert 1 == vertex.id
 +        assert "person" == vertex.label
 +
 +        edge = self.graphson_reader.readObject(
 +            self.graphson_writer.writeObject(Edge(3, Vertex(1, "person"), 
"knows", Vertex(2, "dog"))))
 +        assert "knows" == edge.label
 +        assert 3 == edge.id
 +        assert 1 == edge.outV.id
 +        assert 2 == edge.inV.id
 +
 +        vertex_property = self.graphson_reader.readObject(
 +            self.graphson_writer.writeObject(VertexProperty(1, "age", 32, 
Vertex(1))))
 +        assert 1 == vertex_property.id
 +        assert "age" == vertex_property.key
 +        assert 32 == vertex_property.value
 +
 +        property = 
self.graphson_reader.readObject(self.graphson_writer.writeObject(Property("age",
 32.2, Edge(1,Vertex(2),"knows",Vertex(3)))))
 +        assert "age" == property.key
 +        assert 32.2 == property.value
 +
 +    def test_custom_mapping(self):
 +        # extended mapping
 +        class X(object):
 +            pass
 +
 +        serdes = Mock()
 +        writer = GraphSONWriter(serializer_map={X: serdes})
 +        assert X in writer.serializers
 +
 +        # base dicts are not modified
 +        assert X not in gremlin_python.structure.io.graphsonV3d0._serializers
 +
 +        obj = X()
 +        d = writer.toDict(obj)
 +        serdes.dictify.assert_called_once_with(obj, writer)
 +        assert d is serdes.dictify()
 +
 +        # overridden mapping
 +        serdes = Mock()
 +        writer = GraphSONWriter(serializer_map={int: serdes})
 +        assert gremlin_python.structure.io.graphsonV3d0._serializers[int] is 
not writer.serializers[int]
 +
 +        value = 3
 +        d = writer.toDict(value)
 +        serdes.dictify.assert_called_once_with(value, writer)
 +        assert d is serdes.dictify()
 +
 +    def test_write_long(self):
 +        mapping = self.graphson_writer.toDict(1)
 +        assert mapping['@type'] == 'g:Int32'
 +        assert mapping['@value'] == 1
 +
 +        mapping = self.graphson_writer.toDict(long(1))
 +        assert mapping['@type'] == 'g:Int64'
 +        assert mapping['@value'] == 1
 +
 +    def test_datetime(self):
 +        expected = json.dumps({"@type": "g:Date", "@value": 1481750076295}, 
separators=(',', ':'))
 +        dt = datetime.datetime.utcfromtimestamp(1481750076295 / 1000.0)
 +        output = self.graphson_writer.writeObject(dt)
 +        assert expected == output
 +
 +    def test_timestamp(self):
 +        expected = json.dumps({"@type": "g:Timestamp", "@value": 
1481750076295}, separators=(',', ':'))
 +        ts = timestamp(1481750076295 / 1000.0)
 +        output = self.graphson_writer.writeObject(ts)
 +        assert expected == output
 +
 +    def test_duration(self):
 +        expected = json.dumps({"@type": "gx:Duration", "@value": "P5D"}, 
separators=(',', ':'))
 +        d = datetime.timedelta(hours=120)
 +        output = self.graphson_writer.writeObject(d)
 +        assert expected == output
 +
 +    def test_uuid(self):
 +        expected = json.dumps({'@type': 'g:UUID', '@value': 
"41d2e28a-20a4-4ab0-b379-d810dede3786"}, separators=(',', ':'))
 +        prop = uuid.UUID("41d2e28a-20a4-4ab0-b379-d810dede3786")
 +        output = self.graphson_writer.writeObject(prop)
 +        assert expected == output
 +
 +    def test_bytebuffer(self):
 +        expected = json.dumps({'@type': 'gx:ByteBuffer', '@value': 
'c29tZSBieXRlcyBmb3IgeW91'}, separators=(',', ':'))
 +        bb = ByteBufferType("c29tZSBieXRlcyBmb3IgeW91", "utf8")
 +        output = self.graphson_writer.writeObject(bb)
 +        assert expected == output
 +
 +    def test_char(self):
 +        expected = json.dumps({'@type': 'gx:Char', '@value': 'L'}, 
separators=(',', ':'))
 +        c = str.__new__(SingleChar, chr(76))
 +        output = self.graphson_writer.writeObject(c)
 +        assert expected == output

Reply via email to