Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hugegraph-python-client/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"requests",
"setuptools",
"urllib3",
"rich",
"rich"
]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Removing the trailing comma after "rich" is unrelated to this bug fix and adds diff noise. TOML allows trailing commas; the original style is consistent with the rest of the list. Please revert this hunk.


[project.urls]
Expand Down
14 changes: 8 additions & 6 deletions hugegraph-python-client/src/pyhugegraph/api/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ def addVertices(self, input_data):
return [VertexData({"id": item}) for item in response]
return None

@router.http("PUT", 'graph/vertices/"{vertex_id}"?action=append')
@router.http("PUT", 'graph/vertices/{vertex_id}?action=append')
def appendVertex(self, vertex_id, properties): # pylint: disable=unused-argument
data = {"properties": properties}
if response := self._invoke_request(data=json.dumps(data)):
return VertexData(response)
return None

@router.http("PUT", 'graph/vertices/"{vertex_id}"?action=eliminate')
@router.http("PUT", 'graph/vertices/{vertex_id}?action=eliminate')
def eliminateVertex(self, vertex_id, properties): # pylint: disable=unused-argument
data = {"properties": properties}
if response := self._invoke_request(data=json.dumps(data)):
return VertexData(response)
return None

@router.http("GET", 'graph/vertices/"{vertex_id}"')
@router.http("GET", 'graph/vertices/{vertex_id}')
def getVertexById(self, vertex_id): # pylint: disable=unused-argument
if response := self._invoke_request():
return VertexData(response)
Expand Down Expand Up @@ -101,7 +101,7 @@ def getVertexByCondition(self, label="", limit=0, page=None, properties=None):
return [VertexData(item) for item in response["vertices"]]
return None

@router.http("DELETE", 'graph/vertices/"{vertex_id}"')
@router.http("DELETE", 'graph/vertices/{vertex_id}')
def removeVertexById(self, vertex_id): # pylint: disable=unused-argument
return self._invoke_request()

Expand Down Expand Up @@ -200,8 +200,10 @@ def getVerticesById(self, vertex_ids) -> list[VertexData] | None:
if not vertex_ids:
return []
path = "traversers/vertices?"
for vertex_id in vertex_ids:
path += f'ids="{vertex_id}"&' # pylint: disable=consider-using-join
quoted_vertex_ids = map(json.dumps, vertex_ids)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ json.dumps without ensure_ascii=False will Unicode-escape non-ASCII vertex IDs — json.dumps("\u4eba\u540d") produces '"\\u4eba\\u540d"' instead of '"\u4eba\u540d"', causing HugeGraph lookups to fail for any non-ASCII string vertex ID.

Suggested change
quoted_vertex_ids = (json.dumps(vid, ensure_ascii=False) for vid in vertex_ids)

for vertex_id in quoted_vertex_ids:
path += f'ids={vertex_id}&' # pylint: disable=consider-using-join
path = path.rstrip("&")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ No test added for getVerticesById with numeric IDs. This is the only changed code path in this method but none of the new test cases call it with an integer vertex ID. Please add a test (once the department schema ID strategy is fixed):

def test_get_vertices_by_number_id(self):
    vertex = self.graph.addVertex("department", {"name": "DeptA", "headcount": 10, "floor": 1})
    result = self.graph.getVerticesById([vertex.id])
    self.assertIsNotNone(result)
    self.assertEqual(result[0].id, vertex.id)

if response := self._sess.request(path):
return [VertexData(item) for item in response["vertices"]]
Expand Down
9 changes: 9 additions & 0 deletions hugegraph-python-client/src/pyhugegraph/utils/huge_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import functools
import inspect
import json
import re
import threading
from collections.abc import Callable
Expand Down Expand Up @@ -148,6 +149,14 @@ def wrapper(self: "HGraphContext", *args: Any, **kwargs: Any) -> Any:

all_kwargs["graphspace"] = graphspace_arg or graphspace_cfg
formatted_path = path.format(**all_kwargs)
elif "{vertex_id}" in path:
# fix vertex_id format process
# only quote string type vertex_id
vertex_id = all_kwargs.pop("vertex_id")
if isinstance(vertex_id, str):
vertex_id = "\"" + vertex_id + "\""
all_kwargs['vertex_id'] = vertex_id
Comment on lines +152 to +158
formatted_path = path.format(**all_kwargs)
else:
formatted_path = path.format(**all_kwargs)
else:
Expand Down
27 changes: 27 additions & 0 deletions hugegraph-python-client/src/tests/api/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from ..client_utils import ClientUtils



class TestGraphManager(unittest.TestCase):
client = None
graph = None
Expand Down Expand Up @@ -57,17 +58,33 @@ def test_append_vertex(self):
appended_vertex = self.graph.appendVertex(vertex.id, {"city": "Beijing"})
self.assertEqual(appended_vertex.properties["city"], "Beijing")

def test_append_vertex_with_number_id(self):
vertex = self.graph.addVertex("department", {"name": "DepartmentA", "headcount": 10, "floor": 1})
appended_vertex = self.graph.appendVertex(vertex.id, {"headcount": 15})
self.assertEqual(appended_vertex.properties["headcount"], 15)

def test_eliminate_vertex(self):
vertex = self.graph.addVertex("person", {"name": "marko", "age": 29, "city": "Beijing"})
self.graph.eliminateVertex(vertex.id, {"city": "Beijing"})
eliminated_vertex = self.graph.getVertexById(vertex.id)
self.assertIsNone(eliminated_vertex.properties.get("city"))

def test_eliminate_vertex_with_number_id(self):
vertex = self.graph.addVertex("department", {"name": "DepartmentA", "headcount": 10, "floor": 1})
self.graph.eliminateVertex(vertex.id, {"floor": 1})
eliminated_vertex = self.graph.getVertexById(vertex.id)
self.assertIsNone(eliminated_vertex.properties.get("floor"))

def test_get_vertex_by_id(self):
vertex = self.graph.addVertex("person", {"name": "Alice", "age": 20})
retrieved_vertex = self.graph.getVertexById(vertex.id)
self.assertEqual(retrieved_vertex.id, vertex.id)

def test_get_vertex_by_number_id(self):
vertex = self.graph.addVertex("department", {"name": "DepartmentA", "headcount": 10, "floor": 1})
retrieved_vertex = self.graph.getVertexById(vertex.id)
self.assertEqual(retrieved_vertex.id, vertex.id)

def test_get_vertex_by_page(self):
self.graph.addVertex("person", {"name": "Alice", "age": 20})
self.graph.addVertex("person", {"name": "Bob", "age": 23})
Expand All @@ -89,6 +106,16 @@ def test_remove_vertex_by_id(self):
except NotFoundError as e:
self.assertTrue("Alice\\' does not exist" in str(e))

def test_remove_vertex_by_number_id(self):
vertex = self.graph.addVertex("department", {"name": "DepartmentA", "headcount": 10, "floor": 1})
self.graph.removeVertexById(vertex.id)
try:
self.graph.getVertexById(vertex.id)
except NotFoundError as e:
msg = "\\'{}\\' does not exist".format(vertex.id)
logger.info(f'test_msg: {msg}')
self.assertTrue(msg in str(e))

Comment on lines +111 to +118
def test_add_edge(self):
vertex1 = self.graph.addVertex("person", {"name": "Alice", "age": 20})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ The expected error pattern "\\'{}\\' does not exist" was copied from the string-ID test where IDs have the form label:name. For AUTOMATIC numeric IDs (e.g. 42), HugeGraph's server error format may differ — numeric IDs may not be wrapped in single-quotes in the error message.

Please verify the actual server error text for a numeric vertex ID and update accordingly. Alternatively, drop the message assertion and rely only on assertRaises(NotFoundError) (see also the silent-pass issue already flagged in this block).

vertex2 = self.graph.addVertex("person", {"name": "Bob", "age": 23})
Expand Down
5 changes: 5 additions & 0 deletions hugegraph-python-client/src/tests/client_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def init_property_key(self):
schema.propertyKey("date").asDate().ifNotExist().create()
schema.propertyKey("price").asInt().ifNotExist().create()
schema.propertyKey("weight").asDouble().ifNotExist().create()
schema.propertyKey("headcount").asInt().ifNotExist().create()
schema.propertyKey("floor").asInt().ifNotExist().create()

def init_vertex_label(self):
schema = self.schema
Expand All @@ -68,6 +70,9 @@ def init_vertex_label(self):
schema.vertexLabel("book").useCustomizeStringId().properties("name", "price").nullableKeys(
"price"
).ifNotExist().create()
schema.vertexLabel("department").properties("name", "headcount", "floor").nullableKeys(
"floor"
).ifNotExist().create()

def init_edge_label(self):
schema = self.schema
Expand Down
Loading