You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/08/25 11:50:02 UTC

[tinkerpop] branch TINKERPOP-2407 created (now 9913ae6)

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

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


      at 9913ae6  TINKERPOP-2395 Added deserialization support for dict as keys

This branch includes the following new commits:

     new 99a9bee  Added support for dict as keys, as required by the following example statement: g.V().as_('a').out().as_('b').out().as_('c').dedup('a','b','c').groupCount().by(select('a', 'b').by(valueMap(True))).toList()
     new 9913ae6  TINKERPOP-2395 Added deserialization support for dict as keys

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



[tinkerpop] 02/02: TINKERPOP-2395 Added deserialization support for dict as keys

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9913ae68e72d7e47162d1513da7f9d8cda17aee7
Author: Stephen Mallette <sp...@genoprime.com>
AuthorDate: Tue Aug 25 07:26:32 2020 -0400

    TINKERPOP-2395 Added deserialization support for dict as keys
---
 CHANGELOG.asciidoc                                 |  1 +
 docs/src/upgrade/release-3.5.x.asciidoc            | 14 ++++++++
 .../gremlin_python/structure/io/graphbinaryV1.py   |  3 +-
 .../gremlin_python/structure/io/graphsonV3d0.py    | 25 ++-------------
 .../python/gremlin_python/structure/io/util.py     | 37 ++++++++++++++++++++++
 .../tests/driver/test_driver_remote_connection.py  | 15 +++++----
 6 files changed, 65 insertions(+), 30 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 059b507..2942e1c 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ This release also includes changes from <<release-3-4-3, 3.4.3>>.
 * Ensured better consistency of the use of `null` as arguments to mutation steps.
 * Allowed `property(T.label,Object)` to be used if no value was supplied to `addV(String)`.
 * Allowed additional arguments to `Client.submit()` in Javascript driver to enable setting of parameters like `scriptEvaluationTimeout`.
+* Supported deserialization of `dict` as a key in a `dict` for Python.
 * Added a `Graph.Feature` for `supportsNullPropertyValues`.
 * Modified `TokenTraversal` to support `Property` thus `by(key)` and `by(value)` can now apply to `Edge` and meta-properties.
 * Added `SeedStrategy` to allow deterministic behavior for `coin()`, `sample()` and `Order.shuffle`.
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 635c2d4..2ff012f 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -292,6 +292,20 @@ appropriate token versions automatically.
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-1682[TINKERPOP-1682]
 
+==== Complex dict Deserialization
+
+In Gremlin it is common to return a `dict` as a key value in another `dict`. The problem for Python is that a `dict`
+is not hashable and will result in an error. By introducing a `HashableDict` for those keys, it is now possible to
+return these types of results and not have to work around them:
+
+[source,text]
+----
+>>> g.V().has('person', 'name', 'marko').elementMap("name").groupCount().next()
+{{<T.id: 1>: 1, <T.label: 4>: 'person', 'name': 'marko'}: 1}
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2395[TINKERPOP-2395]
+
 ==== Python 2.x Support
 
 The gremlinpython module no longer supports Python 2.x. Users must use Python 3 going forward. For the most part, from
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
index c1fd32e..a5a258a 100644
--- a/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/graphbinaryV1.py
@@ -38,6 +38,7 @@ from gremlin_python.process.traversal import Barrier, Binding, Bytecode, Cardina
                                              TraversalStrategy, T
 from gremlin_python.process.graph_traversal import GraphTraversal
 from gremlin_python.structure.graph import Graph, Edge, Property, Vertex, VertexProperty, Path
+from gremlin_python.structure.io.util import HashableDict
 
 log = logging.getLogger(__name__)
 
@@ -485,7 +486,7 @@ class MapIO(_GraphBinaryTypeIO):
         size = cls.read_int(b)
         the_dict = {}
         while size > 0:
-            k = r.readObject(b)
+            k = HashableDict.of(r.readObject(b))
             v = r.readObject(b)
             the_dict[k] = v
             size = size - 1
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
index 4d5a288..9ba2127 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
@@ -32,6 +32,7 @@ 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
+from gremlin_python.structure.io.util import HashableDict
 
 log = logging.getLogger(__name__)
 
@@ -41,28 +42,6 @@ log = logging.getLogger(__name__)
 _serializers = OrderedDict()
 _deserializers = {}
 
-
-class hashable_dict(dict):
-    def __hash__(self):
-        try:
-            return hash(tuple(sorted(self.items())))
-        except:
-            return hash(tuple(sorted(str(x) for x in self.items())))
-
-    @classmethod
-    def of(cls, o):
-        if isinstance(o, (tuple, set, list)):
-            return tuple([cls.of(e) for e in o])
-        elif not isinstance(o, (dict, hashable_dict)):
-            return o
-
-        new_o = hashable_dict()
-        for k, v in o.items():
-            new_o[k] = cls.of(v)
-        return new_o
-
-
-
 class GraphSONTypeType(type):
     def __new__(mcs, name, bases, dct):
         cls = super(GraphSONTypeType, mcs).__new__(mcs, name, bases, dct)
@@ -497,7 +476,7 @@ class MapType(_GraphSONTypeIO):
         if len(l) > 0:
             x = 0
             while x < len(l):
-                new_dict[hashable_dict.of(reader.toObject(l[x]))] = reader.toObject(l[x + 1])
+                new_dict[HashableDict.of(reader.toObject(l[x]))] = reader.toObject(l[x + 1])
                 x = x + 2
         return new_dict
 
diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/util.py b/gremlin-python/src/main/python/gremlin_python/structure/io/util.py
new file mode 100644
index 0000000..86fd72a
--- /dev/null
+++ b/gremlin-python/src/main/python/gremlin_python/structure/io/util.py
@@ -0,0 +1,37 @@
+# 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.
+
+
+class HashableDict(dict):
+    def __hash__(self):
+        try:
+            return hash(tuple(sorted(self.items())))
+        except:
+            return hash(tuple(sorted(str(x) for x in self.items())))
+
+    @classmethod
+    def of(cls, o):
+        if isinstance(o, (tuple, set, list)):
+            return tuple([cls.of(e) for e in o])
+        elif not isinstance(o, (dict, HashableDict)):
+            return o
+
+        new_o = HashableDict()
+        for k, v in o.items():
+            new_o[k] = cls.of(v)
+        return new_o
+
diff --git a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
index 663858e..24cdffb 100644
--- a/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
+++ b/gremlin-python/src/main/python/tests/driver/test_driver_remote_connection.py
@@ -18,21 +18,19 @@
 #
 import pytest
 
-from tornado import ioloop, gen
-
 from gremlin_python import statics
 from gremlin_python.driver.protocol import GremlinServerError
 from gremlin_python.statics import long
-from gremlin_python.driver.driver_remote_connection import (
-    DriverRemoteConnection)
 from gremlin_python.process.traversal import Traverser
 from gremlin_python.process.traversal import TraversalStrategy
 from gremlin_python.process.traversal import Bindings
-from gremlin_python.process.traversal import P, Order
+from gremlin_python.process.traversal import P, Order, T
 from gremlin_python.process.graph_traversal import __
 from gremlin_python.process.anonymous_traversal import traversal
 from gremlin_python.structure.graph import Vertex
 from gremlin_python.process.strategies import SubgraphStrategy, ReservedKeysVerificationStrategy, SeedStrategy
+from gremlin_python.structure.io.util import HashableDict
+from gremlin_python.driver.serializer import GraphSONSerializersV2d0
 
 __author__ = 'Marko A. Rodriguez (http://markorodriguez.com)'
 
@@ -99,7 +97,12 @@ class TestDriverRemoteConnection(object):
         # test binding in P
         results = g.V().has('person', 'age', Bindings.of('x', lt(30))).count().next()
         assert 2 == results
-
+        # #
+        # test dict keys which can only work on GraphBinary and GraphSON3 which include specific serialization
+        # types for dict
+        if not isinstance(remote_connection._client._message_serializer, GraphSONSerializersV2d0):
+            results = g.V().has('person', 'name', 'marko').elementMap("name").groupCount().next()
+            assert {HashableDict.of({T.id: 1, T.label: 'person', 'name': 'marko'}): 1} == results
 
     def test_lambda_traversals(self, remote_connection):
         statics.load_statics(globals())


[tinkerpop] 01/02: Added support for dict as keys, as required by the following example statement: g.V().as_('a').out().as_('b').out().as_('c').dedup('a', 'b', 'c').groupCount().by(select('a', 'b').by(valueMap(True))).toList()

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 99a9bee665cf45f6b5079680c7cc28c563ebb4f9
Author: Liran Moysi <li...@gmail.com>
AuthorDate: Wed Jul 22 15:13:25 2020 +0300

    Added support for dict as keys, as required by the following example statement: g.V().as_('a').out().as_('b').out().as_('c').dedup('a','b','c').groupCount().by(select('a', 'b').by(valueMap(True))).toList()
---
 .../gremlin_python/structure/io/graphsonV3d0.py    | 23 +++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV3d0.py
index 21c31ba..4d5a288 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
@@ -42,6 +42,27 @@ _serializers = OrderedDict()
 _deserializers = {}
 
 
+class hashable_dict(dict):
+    def __hash__(self):
+        try:
+            return hash(tuple(sorted(self.items())))
+        except:
+            return hash(tuple(sorted(str(x) for x in self.items())))
+
+    @classmethod
+    def of(cls, o):
+        if isinstance(o, (tuple, set, list)):
+            return tuple([cls.of(e) for e in o])
+        elif not isinstance(o, (dict, hashable_dict)):
+            return o
+
+        new_o = hashable_dict()
+        for k, v in o.items():
+            new_o[k] = cls.of(v)
+        return new_o
+
+
+
 class GraphSONTypeType(type):
     def __new__(mcs, name, bases, dct):
         cls = super(GraphSONTypeType, mcs).__new__(mcs, name, bases, dct)
@@ -476,7 +497,7 @@ class MapType(_GraphSONTypeIO):
         if len(l) > 0:
             x = 0
             while x < len(l):
-                new_dict[reader.toObject(l[x])] = reader.toObject(l[x + 1])
+                new_dict[hashable_dict.of(reader.toObject(l[x]))] = reader.toObject(l[x + 1])
                 x = x + 2
         return new_dict