This is an automated email from the ASF dual-hosted git repository. xiazcy pushed a commit to branch TINKERPOP-3186 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 4f6f80dd69a0871ca5fd6d8a5b10e5d6682e5033 Author: Stephen Mallette <[email protected]> AuthorDate: Thu Sep 4 13:08:28 2025 -0400 TINKERPOP-3186 Normalized python Element.properties to empty list This better matches all the other language variants. --- .../main/python/gremlin_python/structure/graph.py | 2 +- .../gremlin_python/structure/io/graphsonV2d0.py | 6 +-- .../gremlin_python/structure/io/graphsonV3d0.py | 6 +-- .../tests/driver/test_driver_remote_connection.py | 12 ++--- .../python/tests/structure/io/test_graphsonV2d0.py | 58 ++++++++++++++++++++- .../python/tests/structure/io/test_graphsonV3d0.py | 59 +++++++++++++++++++++- .../src/main/python/tests/structure/test_graph.py | 23 +++++++++ 7 files changed, 151 insertions(+), 15 deletions(-) diff --git a/gremlin-python/src/main/python/gremlin_python/structure/graph.py b/gremlin-python/src/main/python/gremlin_python/structure/graph.py index ab83a538e1..2e3dad2727 100644 --- a/gremlin-python/src/main/python/gremlin_python/structure/graph.py +++ b/gremlin-python/src/main/python/gremlin_python/structure/graph.py @@ -34,7 +34,7 @@ class Element(object): def __init__(self, id, label, properties=None): self.id = id self.label = label - self.properties = properties + self.properties = [] if properties is None else properties def __eq__(self, other): return isinstance(other, self.__class__) and self.id == other.id diff --git a/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py b/gremlin-python/src/main/python/gremlin_python/structure/io/graphsonV2d0.py index 08c82c5e45..7ebffd01b9 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 @@ -607,7 +607,7 @@ class VertexDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: @@ -620,7 +620,7 @@ class EdgeDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: @@ -637,7 +637,7 @@ class VertexPropertyDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: 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 75bc642b26..66a1fb0d7f 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 @@ -704,7 +704,7 @@ class VertexDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: @@ -717,7 +717,7 @@ class EdgeDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: @@ -734,7 +734,7 @@ class VertexPropertyDeserializer(_GraphSONTypeIO): @classmethod def objectify(cls, d, reader): - properties = None + properties = [] if "properties" in d: properties = reader.to_object(d["properties"]) if properties is not None: 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 346d5ffbb3..80aaa272ef 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 @@ -116,24 +116,24 @@ class TestDriverRemoteConnection(object): # test materializeProperties in V - GraphSON will deserialize into None and GraphBinary to [] results = g.with_("materializeProperties", "tokens").V().to_list() for v in results: - assert v.properties is None or len(v.properties) == 0 + assert len(v.properties) == 0 # # # test materializeProperties in E - GraphSON will deserialize into None and GraphBinary to [] results = g.with_("materializeProperties", "tokens").E().to_list() for e in results: - assert e.properties is None or len(e.properties) == 0 + assert len(e.properties) == 0 # # # test materializeProperties in VP - GraphSON will deserialize into None and GraphBinary to [] results = g.with_("materializeProperties", "tokens").V().properties().to_list() for vp in results: - assert vp.properties is None or len(vp.properties) == 0 + assert len(vp.properties) == 0 # # # test materializeProperties in Path - GraphSON will deserialize into None and GraphBinary to [] p = g.with_("materializeProperties", "tokens").V().has('name', 'marko').outE().inV().has_label('software').path().next() assert 3 == len(p.objects) - assert p.objects[0].properties is None or len(p.objects[0].properties) == 0 - assert p.objects[1].properties is None or len(p.objects[1].properties) == 0 - assert p.objects[2].properties is None or len(p.objects[2].properties) == 0 + assert len(p.objects[0].properties) == 0 + assert len(p.objects[1].properties) == 0 + assert len(p.objects[2].properties) == 0 # # # test materializeProperties in Path - 'all' should materialize properties on each element p = g.with_("materializeProperties", "all").V().has('name', 'marko').outE().inV().has_label('software').path().next() diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV2d0.py index 8b31970605..d6ff7cd7b0 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 @@ -154,13 +154,16 @@ class TestGraphSONReader: def test_graph(self): vertex = self.graphson_reader.read_object(""" - {"@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" [...] + {"@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"}}]}}}""") assert isinstance(vertex, Vertex) assert "person" == vertex.label assert 1 == vertex.id assert isinstance(vertex.id, int) assert vertex == Vertex(1) assert 2 == len(vertex.properties) + # assert actual property values - Vertex.properties should be VertexProperty objects + assert any(vp.label == 'name' and vp.value == 'marko' for vp in vertex.properties) + assert any(vp.label == 'age' and vp.value == 29 for vp in vertex.properties) ## vertex = self.graphson_reader.read_object(""" {"@type":"g:Vertex", "@value":{"id":{"@type":"g:Float","@value":45.23}}}""") @@ -169,6 +172,18 @@ class TestGraphSONReader: assert isinstance(vertex.id, FloatType) assert "vertex" == vertex.label assert vertex == Vertex(45.23) + # properties key omitted should yield empty list + assert vertex.properties == [] + ## + # vertex with explicit label and without properties + vertex = self.graphson_reader.read_object( + """ + {"@type":"g:Vertex", "@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}} + """) + assert isinstance(vertex, Vertex) + assert vertex.label == 'person' + assert vertex.id == 2 + assert vertex.properties == [] ## vertex_property = self.graphson_reader.read_object(""" {"@type":"g:VertexProperty", "@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""") @@ -177,6 +192,8 @@ class TestGraphSONReader: assert "aKey" == vertex_property.label assert vertex_property.value assert vertex_property.vertex == Vertex(9) + # no properties key should yield empty list of meta-properties + assert vertex_property.properties == [] ## vertex_property = self.graphson_reader.read_object(""" {"@type":"g:VertexProperty", "@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""") @@ -185,6 +202,8 @@ class TestGraphSONReader: assert "name" == vertex_property.label assert "marko" == vertex_property.value assert vertex_property.vertex is None + # no properties key should yield empty list of meta-properties + assert vertex_property.properties == [] ## edge = self.graphson_reader.read_object(""" {"@type":"g:Edge", "@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""") @@ -195,6 +214,14 @@ class TestGraphSONReader: assert edge.inV == Vertex("x", "xLabel") assert edge.outV == Vertex("y", "vertex") ## + # edge without properties should yield empty properties list + edge2 = self.graphson_reader.read_object( + """ + {"@type":"g:Edge", "@value":{"id":{"@type":"g:Int64","@value":18},"label":"knows","inV":"a","outV":"b","inVLabel":"xLab"}} + """) + assert isinstance(edge2, Edge) + assert edge2.properties == [] + ## property = self.graphson_reader.read_object(""" {"@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 @@ -204,6 +231,7 @@ class TestGraphSONReader: assert Edge(122, Vertex("x"), "knows", Vertex("y")) == property.element def test_path(self): + # original path with vertices that include properties path = self.graphson_reader.read_object( """{"@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", [...] ) @@ -213,6 +241,34 @@ class TestGraphSONReader: assert Vertex(1) == path["a"] assert "lop" == path[2] assert 3 == len(path) + # ensure element properties were populated from GraphSON + assert any(vp.label == 'name' and vp.value == 'marko' for vp in path[0].properties) + assert any(vp.label == 'age' and vp.value == 29 for vp in path[0].properties) + assert any(vp.label == 'name' and vp.value == 'lop' for vp in path[1].properties) + assert any(vp.label == 'lang' and vp.value == 'java' for vp in path[1].properties) + + # path with elements that exclude properties (no "properties" key) + path2 = self.graphson_reader.read_object( + """{"@type":"g:Path","@value":{"labels":[["x"],["y"],["z"]],"objects":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":11},"label":"person"}},{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":77},"label":"knows","outV":{"@type":"g:Int32","@value":11},"outVLabel":"person","inV":{"@type":"g:Int32","@value":12},"inVLabel":"person"}},"hello"]}}""" + ) + assert isinstance(path2, Path) + assert 3 == len(path2) + assert Vertex(11, 'person') == path2[0] + assert path2[0].properties == [] + assert isinstance(path2[1], Edge) + assert path2[1].properties == [] + assert path2[2] == "hello" + + # mixed path: first vertex with properties, second vertex without + path3 = self.graphson_reader.read_object( + """{"@type":"g:Path","@value":{"labels":[["a"],["b"]],"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"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}]}}""" + ) + assert isinstance(path3, Path) + assert 2 == len(path3) + assert Vertex(1) == path3[0] + assert any(vp.label == 'name' and vp.value == 'marko' for vp in path3[0].properties) + assert Vertex(2) == path3[1] + assert path3[1].properties == [] def test_custom_mapping(self): diff --git a/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py b/gremlin-python/src/main/python/tests/structure/io/test_graphsonV3d0.py index 099c1ab37c..91c1e6d7a7 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 @@ -199,12 +199,16 @@ class TestGraphSONReader: def test_graph(self): vertex = self.graphson_reader.read_object(""" - {"@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" [...] + {"@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"}}]}}}""") assert isinstance(vertex, Vertex) assert "person" == vertex.label assert 1 == vertex.id assert isinstance(vertex.id, int) assert vertex == Vertex(1) + assert 2 == len(vertex.properties) + # assert actual property values - Vertex.properties should be VertexProperty objects + assert any(vp.label == 'name' and vp.value == 'marko' for vp in vertex.properties) + assert any(vp.label == 'age' and vp.value == 29 for vp in vertex.properties) ## vertex = self.graphson_reader.read_object(""" {"@type":"g:Vertex", "@value":{"id":{"@type":"g:Float","@value":45.23}}}""") @@ -213,6 +217,18 @@ class TestGraphSONReader: assert isinstance(vertex.id, FloatType) assert "vertex" == vertex.label assert vertex == Vertex(45.23) + # properties key omitted should yield empty list + assert vertex.properties == [] + ## + # vertex with explicit label and without properties + vertex = self.graphson_reader.read_object( + """ + {"@type":"g:Vertex", "@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}} + """) + assert isinstance(vertex, Vertex) + assert vertex.label == 'person' + assert vertex.id == 2 + assert vertex.properties == [] ## vertex_property = self.graphson_reader.read_object(""" {"@type":"g:VertexProperty", "@value":{"id":"anId","label":"aKey","value":true,"vertex":{"@type":"g:Int32","@value":9}}}""") @@ -221,6 +237,8 @@ class TestGraphSONReader: assert "aKey" == vertex_property.label assert vertex_property.value assert vertex_property.vertex == Vertex(9) + # no properties key should yield empty list of meta-properties + assert vertex_property.properties == [] ## vertex_property = self.graphson_reader.read_object(""" {"@type":"g:VertexProperty", "@value":{"id":{"@type":"g:Int32","@value":1},"label":"name","value":"marko"}}""") @@ -229,6 +247,8 @@ class TestGraphSONReader: assert "name" == vertex_property.label assert "marko" == vertex_property.value assert vertex_property.vertex is None + # no properties key should yield empty list of meta-properties + assert vertex_property.properties == [] ## edge = self.graphson_reader.read_object(""" {"@type":"g:Edge", "@value":{"id":{"@type":"g:Int64","@value":17},"label":"knows","inV":"x","outV":"y","inVLabel":"xLab","properties":{"aKey":"aValue","bKey":true}}}""") @@ -239,6 +259,14 @@ class TestGraphSONReader: assert edge.inV == Vertex("x", "xLabel") assert edge.outV == Vertex("y", "vertex") ## + # edge without properties should yield empty properties list + edge2 = self.graphson_reader.read_object( + """ + {"@type":"g:Edge", "@value":{"id":{"@type":"g:Int64","@value":18},"label":"knows","inV":"a","outV":"b","inVLabel":"xLab"}} + """) + assert isinstance(edge2, Edge) + assert edge2.properties == [] + ## property = self.graphson_reader.read_object(""" {"@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 @@ -248,6 +276,7 @@ class TestGraphSONReader: assert Edge(122, Vertex("x"), "knows", Vertex("y")) == property.element def test_path(self): + # original path with vertices that include properties path = self.graphson_reader.read_object( """{"@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": [...] ) @@ -257,6 +286,34 @@ class TestGraphSONReader: assert Vertex(1) == path["a"] assert "lop" == path[2] assert 3 == len(path) + # ensure element properties were populated from GraphSON + assert any(vp.label == 'name' and vp.value == 'marko' for vp in path[0].properties) + assert any(vp.label == 'age' and vp.value == 29 for vp in path[0].properties) + assert any(vp.label == 'name' and vp.value == 'lop' for vp in path[1].properties) + assert any(vp.label == 'lang' and vp.value == 'java' for vp in path[1].properties) + + # path with elements that exclude properties (no "properties" key) + path2 = self.graphson_reader.read_object( + """{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["x"]},{"@type":"g:Set","@value":["y"]},{"@type":"g:Set","@value":["z"]}]},"objects":{"@type":"g:List","@value":[{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":11},"label":"person"}},{"@type":"g:Edge","@value":{"id":{"@type":"g:Int64","@value":77},"label":"knows","outV":{"@type":"g:Int32","@value":11},"outVLabel":"person","inV":{"@type":"g:Int32","@value":12},"inVLabe [...] + ) + assert isinstance(path2, Path) + assert 3 == len(path2) + assert Vertex(11, 'person') == path2[0] + assert path2[0].properties == [] + assert isinstance(path2[1], Edge) + assert path2[1].properties == [] + assert path2[2] == "hello" + + # mixed path: first vertex with properties, second vertex without + path3 = self.graphson_reader.read_object( + """{"@type":"g:Path","@value":{"labels":{"@type":"g:List","@value":[{"@type":"g:Set","@value":["a"]},{"@type":"g:Set","@value":["b"]}]},"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"}}]}}},{"@type":"g:Vertex","@value":{"id":{"@type":"g:Int32","@value":2},"label":"person"}}]}}}""" + ) + assert isinstance(path3, Path) + assert 2 == len(path3) + assert Vertex(1) == path3[0] + assert any(vp.label == 'name' and vp.value == 'marko' for vp in path3[0].properties) + assert Vertex(2) == path3[1] + assert path3[1].properties == [] def test_custom_mapping(self): diff --git a/gremlin-python/src/main/python/tests/structure/test_graph.py b/gremlin-python/src/main/python/tests/structure/test_graph.py index 0937e3998b..8b7fe38300 100644 --- a/gremlin-python/src/main/python/tests/structure/test_graph.py +++ b/gremlin-python/src/main/python/tests/structure/test_graph.py @@ -34,6 +34,8 @@ class TestGraph(object): assert "vertex" == vertex.label assert "person" == Vertex(1, "person").label assert vertex == Vertex(1) + # properties default to empty list when not provided + assert vertex.properties == [] # edge = Edge(2, Vertex(1), "said", Vertex("hello", "phrase")) assert "e[2][1-said->hello]" == str(edge) @@ -42,6 +44,8 @@ class TestGraph(object): assert "said" == edge.label assert "phrase" == edge.inV.label assert edge.inV != edge.outV + # properties default to empty list when not provided + assert edge.properties == [] # vertex_property = VertexProperty(long(24), "name", "marko", Vertex(1)) assert "vp[name->marko]" == str(vertex_property) @@ -52,6 +56,8 @@ class TestGraph(object): assert Vertex(1) == vertex_property.vertex assert isinstance(vertex_property.id, long) assert vertex_property == VertexProperty(long(24), "name", "marko", Vertex(1)) + # meta-properties default to empty list when not provided + assert vertex_property.properties == [] # property = Property("age", 29, Vertex(1)) assert "p[age->29]" == str(property) @@ -61,6 +67,23 @@ class TestGraph(object): assert isinstance(property.value, int) assert property == Property("age", 29, Vertex(1)) # + # Now create elements with properties explicitly set + v2 = Vertex(10, "person", [VertexProperty(100, "name", "marko", Vertex(10))]) + assert len(v2.properties) == 1 + assert isinstance(v2.properties[0], VertexProperty) + assert v2.properties[0].label == "name" + assert v2.properties[0].value == "marko" + e2 = Edge(20, Vertex(10), "knows", Vertex(11), [Property("weight", 0.5, None)]) + assert len(e2.properties) == 1 + assert isinstance(e2.properties[0], Property) + assert e2.properties[0].key == "weight" + assert e2.properties[0].value == 0.5 + vp2 = VertexProperty(30, "name", "marko", Vertex(10), [Property("since", 2006, None)]) + assert len(vp2.properties) == 1 + assert isinstance(vp2.properties[0], Property) + assert vp2.properties[0].key == "since" + assert vp2.properties[0].value == 2006 + # for i in [vertex, edge, vertex_property, property]: for j in [vertex, edge, vertex_property, property]: if type(i) != type(j):
