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