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()