You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by da...@apache.org on 2017/11/01 23:41:58 UTC

tinkerpop git commit: Implemented support for missing core GraphSON types in gremlin-python

Repository: tinkerpop
Updated Branches:
  refs/heads/TINKERPOP-1807 [created] 3b651ff58


Implemented support for missing core GraphSON types in gremlin-python


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/3b651ff5
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/3b651ff5
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/3b651ff5

Branch: refs/heads/TINKERPOP-1807
Commit: 3b651ff58cdf210a63a0101d0a311c7076de2b0e
Parents: f2b4fb9
Author: davebshow <da...@gmail.com>
Authored: Wed Nov 1 16:31:10 2017 -0700
Committer: davebshow <da...@gmail.com>
Committed: Wed Nov 1 16:31:10 2017 -0700

----------------------------------------------------------------------
 .../src/main/jython/gremlin_python/statics.py   |  9 +++
 .../gremlin_python/structure/io/graphson.py     | 71 ++++++++++++++++-
 .../jython/tests/structure/io/test_graphson.py  | 81 +++++++++++++++++++-
 3 files changed, 156 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3b651ff5/gremlin-python/src/main/jython/gremlin_python/statics.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/statics.py b/gremlin-python/src/main/jython/gremlin_python/statics.py
index a1abf8e..f98347e 100644
--- a/gremlin-python/src/main/jython/gremlin_python/statics.py
+++ b/gremlin-python/src/main/jython/gremlin_python/statics.py
@@ -36,6 +36,15 @@ else:
     from types import LongType
     from types import TypeType
 
+
+class timestamp(float):
+    """
+    In Python a timestamp is simply a float. This dummy class (similar to long), allows users to wrap a float
+    in a GLV script to make sure the value is serialized as a GraphSON timestamp.
+    """
+    pass
+
+
 staticMethods = {}
 staticEnums = {}
 default_lambda_language = "gremlin-python"

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3b651ff5/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py
index 0daeffa..04085ee 100644
--- a/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py
+++ b/gremlin-python/src/main/jython/gremlin_python/structure/io/graphson.py
@@ -16,11 +16,15 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 '''
-from aenum import Enum
+import datetime
 import json
-import six
+import time
+import uuid
 from collections import OrderedDict
 
+import six
+from aenum import Enum
+
 from gremlin_python import statics
 from gremlin_python.statics import FloatType, FunctionType, IntType, LongType, TypeType
 from gremlin_python.process.traversal import Binding, Bytecode, P, Traversal, Traverser, TraversalStrategy
@@ -298,6 +302,69 @@ class TypeSerializer(_GraphSONTypeIO):
         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):
+        # Java timestamp expects miliseconds
+        if six.PY3:
+            pts = obj.timestamp()
+        else:
+            # Hack for legacy Python
+            # Taken from:
+            # https://github.com/jaraco/backports.datetime_timestamp/blob/master/backports/datetime_timestamp/__init__.py
+            pts = time.mktime((obj.year, obj.month, obj.day,
+			                   obj.hour, obj.minute, obj.second,
+			                   -1, -1, -1)) + obj.microsecond / 1e6
+
+        # Have to use int because of legacy Python
+        ts = int(round(pts * 1000))
+        return GraphSONUtil.typedValue(cls.graphson_base_type, ts)
+
+    @classmethod
+    def objectify(cls, ts, reader):
+        # Python timestamp expects seconds
+        return datetime.datetime.fromtimestamp(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):

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/3b651ff5/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py
----------------------------------------------------------------------
diff --git a/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py b/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py
index d80d045..75e7136 100644
--- a/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py
+++ b/gremlin-python/src/main/jython/tests/structure/io/test_graphson.py
@@ -18,16 +18,20 @@ under the License.
 '''
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
-
+import datetime
 import json
+import uuid
+
 from mock import Mock
 
 import six
 
 from gremlin_python.statics import *
-from gremlin_python.structure.graph import Vertex, Edge, VertexProperty, Property
+from gremlin_python.structure.graph import (
+    Vertex, Edge, VertexProperty, Property, Graph)
 from gremlin_python.structure.graph import Path
-from gremlin_python.structure.io.graphson import GraphSONWriter, GraphSONReader, GraphSONUtil
+from gremlin_python.structure.io.graphson import (
+    GraphSONWriter, GraphSONReader, GraphSONUtil)
 import gremlin_python.structure.io.graphson
 from gremlin_python.process.traversal import P
 from gremlin_python.process.strategies import SubgraphStrategy
@@ -121,6 +125,19 @@ class TestGraphSONReader(object):
         serdes.objectify.assert_called_once_with(value, reader)
         assert o is serdes.objectify()
 
+    def test_datetime(self):
+        dt = self.graphson_reader.readObject(json.dumps({"@type": "g:Date", "@value": 1481750076295}))
+        assert isinstance(dt, datetime.datetime)
+
+    def test_timestamp(self):
+        dt = self.graphson_reader.readObject(json.dumps({"@type": "g:Timestamp", "@value": 1481750076295}))
+        assert isinstance(dt, timestamp)
+
+    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)
+
 
 class TestGraphSONWriter(object):
     graphson_writer = GraphSONWriter()
@@ -224,3 +241,61 @@ class TestGraphSONWriter(object):
         property = self.graphson_reader.readObject(self.graphson_writer.writeObject(Property("age", 32.2)))
         assert "age" == property.key
         assert 32.2 == property.value
+
+    def test_datetime(self):
+        expected = json.dumps({"@type": "g:Date", "@value": 1481750076295}, separators=(',', ':'))
+        dt = datetime.datetime.fromtimestamp(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_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
+
+class TestFunctionalGraphSONIO(object):
+    """Functional IO tests"""
+
+    def test_timestamp(self, remote_connection):
+        g = Graph().traversal().withRemote(remote_connection)
+        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
+        finally:
+            g.V(vid).drop().iterate()
+
+    def test_datetime(self, remote_connection):
+        g = Graph().traversal().withRemote(remote_connection)
+        dt = datetime.datetime.fromtimestamp(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
+        finally:
+            g.V(vid).drop().iterate()
+
+    def test_uuid(self, remote_connection):
+        g = Graph().traversal().withRemote(remote_connection)
+        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
+        finally:
+            g.V(vid).drop().iterate()