You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/04/29 12:26:23 UTC

[tinkerpop] branch master updated (699ea53 -> 2453536)

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

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


    from 699ea53  Removed sideEffect related infrastructure in TraversalOpProcesssor
     add 7210d7a  TINKERPOP-2360 gremlin-python: fix up serializer out of range of int
     new afe9d70  TINKERPOP-2360 Added some extra assertions around numbers
     new a3cefa0  Merge branch '3.3-dev' into 3.4-dev
     new 2453536  Merge branch '3.4-dev'

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 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(-)


[tinkerpop] 02/03: Merge branch '3.3-dev' into 3.4-dev

Posted by sp...@apache.org.
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 a3cefa0aec588deaf508234d51c0ede160c1e9ea
Merge: 58bd8f4 afe9d70
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Wed Apr 29 08:11:20 2020 -0400

    Merge branch '3.3-dev' into 3.4-dev

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



[tinkerpop] 03/03: Merge branch '3.4-dev'

Posted by sp...@apache.org.
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


[tinkerpop] 01/03: TINKERPOP-2360 Added some extra assertions around numbers

Posted by sp...@apache.org.
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 afe9d70dd207e2d673f9f77a3280b9c1d87e1837
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Wed Apr 29 08:09:45 2020 -0400

    TINKERPOP-2360 Added some extra assertions around numbers
    
    Should now cover all the number ranges in GraphSON CTR
---
 CHANGELOG.asciidoc                                             |  2 +-
 .../src/main/jython/tests/structure/io/test_graphsonV2d0.py    | 10 ++++------
 .../src/main/jython/tests/structure/io/test_graphsonV3d0.py    | 10 ++++------
 3 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 19be040..ce200c1 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -33,7 +33,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Bumped to Jackson 2.9.10.4.
 * Remove invalid service descriptors from gremlin-shaded.
 * Fixed bug in Python and .NET traversal `clone()` where deep copies of bytecode were not occurring.
-* Fixed bug in Python about integer serializer which out of range of Int32
+* Fixed bug in Python about integer serializer which was out of range of `g:Int32`
 
 [[release-3-3-10]]
 === TinkerPop 3.3.10 (Release Date: February 3, 2020)
diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py
index e9801af..fa8d8bf 100644
--- a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py
+++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV2d0.py
@@ -297,16 +297,14 @@ 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')))
diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py
index 5010891..200e014 100644
--- a/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py
+++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphsonV3d0.py
@@ -348,16 +348,14 @@ class TestGraphSONWriter(object):
                 "@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')))