From a24f98f433fef14494c0afc88c12ac297c9c0677 Mon Sep 17 00:00:00 2001 From: Yanli Date: Thu, 30 May 2024 18:02:28 +0800 Subject: [PATCH 1/8] Update execute_py --- example/Params.py | 4 ++-- nebula3/gclient/net/base.py | 27 +++++++++++++++++++++++---- tests/test_parameter.py | 8 ++++---- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/example/Params.py b/example/Params.py index 940053c1..0dd9c28a 100644 --- a/example/Params.py +++ b/example/Params.py @@ -51,12 +51,12 @@ "p4": ["Bob", "Lily"], } -resp = client.execute_py_params( +resp = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", params_premitive, ) -resp = client.execute_py_params( +resp = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", params_premitive, ) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index 8763410a..55028740 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -24,11 +24,30 @@ def execute(self, stmt: str) -> ResultSet: def execute_json(self, stmt: str) -> bytes: return self.execute_json_with_parameter(stmt, None) - def execute_py_params( - self, stmt: str, params: Optional[Dict[str, Any]] - ) -> ResultSet: + def execute_py(self, stmt: str, params: Optional[Dict[str, Any]]): """**Recommended** Execute a statement with parameters in Python type instead of thrift type.""" - return self.execute_parameter(stmt, _build_byte_param(params)) + if params is None: + result = self.execute_parameter(stmt, None) + else: + result = self.execute_parameter(stmt, _build_byte_param(params)) + + if not result.is_succeeded(): + raise Exception( + "NebulaGraph query failed:", + result.error_msg(), + "Statement:", + stmt, + "Params:", + params, + ) + full_result = [ + { + str(key): result.row_values(row_index)[i].cast_primitive() + for i, key in enumerate(result.keys()) + } + for row_index in range(result.row_size()) + ] + return full_result def _build_byte_param(params: dict) -> dict: diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 0fd04e0b..db0c5397 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -95,7 +95,7 @@ def test_parameter(self): assert "bob1" == resp.row_values(0)[2].as_string() # same test with premitive params - resp = client.execute_py_params( + resp = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", self.params_premitive, ) @@ -126,7 +126,7 @@ def test_parameter(self): self.params, ) assert not resp.is_succeeded() - resp = client.execute_py_params( + resp = client.execute_py( '$p1=go from "Bob" over like yield like._dst;', self.params_premitive, ) @@ -136,7 +136,7 @@ def test_parameter(self): self.params, ) assert not resp.is_succeeded() - resp = client.execute_py_params( + resp = client.execute_py( "go from $p3 over like yield like._dst;", self.params_premitive, ) @@ -162,7 +162,7 @@ def test_parameter(self): ) assert not resp.is_succeeded() - resp = client.execute_py_params( + resp = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", self.params_premitive, ) From ad76c846ca567a22999833635639b33b1fa0a27b Mon Sep 17 00:00:00 2001 From: Yanli Date: Thu, 30 May 2024 18:13:56 +0800 Subject: [PATCH 2/8] update test --- tests/test_parameter.py | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/tests/test_parameter.py b/tests/test_parameter.py index db0c5397..47369e11 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -23,7 +23,8 @@ def setUp(self) -> None: self.configs = Config() self.configs.max_connection_pool_size = 6 self.pool = ConnectionPool() - self.pool.init([("127.0.0.1", 9671)], self.configs) + # self.pool.init([("127.0.0.1", 9671)], self.configs) + self.pool.init([("127.0.0.1", 9669)], self.configs) # get session from the pool client = self.pool.get_session("root", "nebula") @@ -99,13 +100,11 @@ def test_parameter(self): "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", self.params_premitive, ) - assert resp.is_succeeded(), resp.error_msg() - assert 1 == resp.row_size() - names = ["col1", "col2", "col3"] - assert names == resp.keys() - assert 6 == resp.row_values(0)[0].as_int() - assert False == resp.row_values(0)[1].as_bool() - assert "bob1" == resp.row_values(0)[2].as_string() + assert 1 == len(resp) + assert ["col1", "col2", "col3"] == list(resp[0].keys()) + assert resp[0]["col1"] == 6 + assert resp[0]["col2"] == False + assert resp[0]["col3"] == "bob1" # test cypher parameter resp = client.execute_parameter( f"""MATCH (v:person)--() WHERE v.person.age>abs($p1)+3 @@ -126,21 +125,27 @@ def test_parameter(self): self.params, ) assert not resp.is_succeeded() - resp = client.execute_py( - '$p1=go from "Bob" over like yield like._dst;', - self.params_premitive, - ) - assert not resp.is_succeeded() + try: + resp = client.execute_py( + '$p1=go from "Bob" over like yield like._dst;', + self.params_premitive, + ) + raise AssertionError("should raise exception") + except: + pass resp = client.execute_parameter( "go from $p3 over like yield like._dst;", self.params, ) assert not resp.is_succeeded() - resp = client.execute_py( - "go from $p3 over like yield like._dst;", - self.params_premitive, - ) - assert not resp.is_succeeded() + try: + resp = client.execute_py( + "go from $p3 over like yield like._dst;", + self.params_premitive, + ) + raise AssertionError("should raise exception") + except: + pass resp = client.execute_parameter( "fetch prop on person $p3 yield vertex as v", self.params, @@ -166,8 +171,7 @@ def test_parameter(self): "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", self.params_premitive, ) - assert resp.is_succeeded(), resp.error_msg() - assert 2 == resp.row_size() + assert 2 == len(resp) def tearDown(self) -> None: client = self.pool.get_session("root", "nebula") From 3648f868acc6328a3b58e0e9c8ccf2ed9ffbd7ac Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:03:08 +0800 Subject: [PATCH 3/8] update --- README.md | 8 ++--- example/Params.py | 8 +++-- nebula3/gclient/net/__init__.py | 1 + nebula3/gclient/net/base.py | 52 ++++++++++++++++++++++++------ tests/test_parameter.py | 56 +++++++++++++++++---------------- 5 files changed, 81 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 393c9552..cbf01b49 100644 --- a/README.md +++ b/README.md @@ -137,14 +137,14 @@ params = { "ids": ["player100", "player101"], # second query } -result = client.execute_py_params( - "RETURN abs($p1)+3 AS col1, (toBoolean($p2) AND false) AS col2, toLower($p3)+1 AS col3", +resp: List[Dict[str, Any]] = client.execute_py( + "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", params, ) - -result = client.execute_py_params( +resp: ResultSet = client.execute_py( "MATCH (v) WHERE id(v) in $ids RETURN id(v) AS vertex_id", params, + primitive_res=False, ) ``` diff --git a/example/Params.py b/example/Params.py index 0dd9c28a..671fd0fe 100644 --- a/example/Params.py +++ b/example/Params.py @@ -1,8 +1,10 @@ import time +from typing import Any, Dict, List from nebula3.gclient.net import ConnectionPool from nebula3.Config import Config from nebula3.common import ttypes +from nebula3.data.ResultSet import ResultSet # define a config config = Config() @@ -51,12 +53,12 @@ "p4": ["Bob", "Lily"], } -resp = client.execute_py( +resp: List[Dict[str, Any]] = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", params_premitive, ) - -resp = client.execute_py( +resp: ResultSet = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", params_premitive, + primitive_res=False, ) diff --git a/nebula3/gclient/net/__init__.py b/nebula3/gclient/net/__init__.py index 527d5c79..6ee86442 100644 --- a/nebula3/gclient/net/__init__.py +++ b/nebula3/gclient/net/__init__.py @@ -20,3 +20,4 @@ from nebula3.gclient.net.Session import Session from nebula3.gclient.net.Connection import Connection from nebula3.gclient.net.ConnectionPool import ConnectionPool +from nebula3.gclient.net.base import BaseExecutor, ExecuteError diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index 55028740..f990d962 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -1,10 +1,25 @@ import datetime from abc import abstractmethod -from typing import Dict, Any, Optional +from typing import Dict, Any, Optional, overload, Literal, List from nebula3.data.ResultSet import ResultSet from nebula3.common.ttypes import ErrorCode, Value, NList, Date, Time, DateTime +class ExecuteError(Exception): + def __init__(self, stmt: str, param: Any, code: ErrorCode, msg: str): + self.stmt = stmt + self.param = param + self.code = code + self.msg = msg + + def __str__(self): + return ( + f"ExecuteError. err_code: {self.code}, err_msg: {self.msg}.\n" + + f"Statement: \n{self.stmt}\n" + + f"Parameter: \n{self.param}" + ) + + class BaseExecutor: @abstractmethod def execute_parameter( @@ -24,7 +39,28 @@ def execute(self, stmt: str) -> ResultSet: def execute_json(self, stmt: str) -> bytes: return self.execute_json_with_parameter(stmt, None) - def execute_py(self, stmt: str, params: Optional[Dict[str, Any]]): + @overload + def execute_py( + self, + stmt: str, + params: Optional[Dict[str, Any]] = None, + primitive_res: Literal[True] = True, + ) -> List[Dict[str, Any]]: ... + + @overload + def execute_py( + self, + stmt: str, + params: Optional[Dict[str, Any]] = None, + primitive_res: Literal[False] = False, + ) -> ResultSet: ... + + def execute_py( + self, + stmt: str, + params: Optional[Dict[str, Any]] = None, + primitive_res: bool = True, + ): """**Recommended** Execute a statement with parameters in Python type instead of thrift type.""" if params is None: result = self.execute_parameter(stmt, None) @@ -32,14 +68,10 @@ def execute_py(self, stmt: str, params: Optional[Dict[str, Any]]): result = self.execute_parameter(stmt, _build_byte_param(params)) if not result.is_succeeded(): - raise Exception( - "NebulaGraph query failed:", - result.error_msg(), - "Statement:", - stmt, - "Params:", - params, - ) + raise ExecuteError(stmt, params, result.error_code(), result.error_msg()) + if not primitive_res: + return result + full_result = [ { str(key): result.row_values(row_index)[i].cast_primitive() diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 47369e11..c57c446f 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -8,7 +8,7 @@ import time import json -from nebula3.gclient.net import ConnectionPool +from nebula3.gclient.net import ConnectionPool, ExecuteError from nebula3.Config import Config from nebula3.common import * from unittest import TestCase @@ -95,16 +95,6 @@ def test_parameter(self): assert False == resp.row_values(0)[1].as_bool() assert "bob1" == resp.row_values(0)[2].as_string() - # same test with premitive params - resp = client.execute_py( - "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", - self.params_premitive, - ) - assert 1 == len(resp) - assert ["col1", "col2", "col3"] == list(resp[0].keys()) - assert resp[0]["col1"] == 6 - assert resp[0]["col2"] == False - assert resp[0]["col3"] == "bob1" # test cypher parameter resp = client.execute_parameter( f"""MATCH (v:person)--() WHERE v.person.age>abs($p1)+3 @@ -125,27 +115,11 @@ def test_parameter(self): self.params, ) assert not resp.is_succeeded() - try: - resp = client.execute_py( - '$p1=go from "Bob" over like yield like._dst;', - self.params_premitive, - ) - raise AssertionError("should raise exception") - except: - pass resp = client.execute_parameter( "go from $p3 over like yield like._dst;", self.params, ) assert not resp.is_succeeded() - try: - resp = client.execute_py( - "go from $p3 over like yield like._dst;", - self.params_premitive, - ) - raise AssertionError("should raise exception") - except: - pass resp = client.execute_parameter( "fetch prop on person $p3 yield vertex as v", self.params, @@ -167,6 +141,34 @@ def test_parameter(self): ) assert not resp.is_succeeded() + # same test with premitive params + resp = client.execute_py( + "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", + self.params_premitive, + ) + assert 1 == len(resp) + assert ["col1", "col2", "col3"] == list(resp[0].keys()) + assert resp[0]["col1"] == 6 + assert resp[0]["col2"] == False + assert resp[0]["col3"] == "bob1" + try: + resp = client.execute_py( + '$p1=go from "Bob" over like yield like._dst;', + self.params_premitive, + ) + except ExecuteError: + pass + else: + raise AssertionError("should raise exception") + try: + resp = client.execute_py( + "go from $p3 over like yield like._dst;", + self.params_premitive, + ) + except ExecuteError: + pass + else: + raise AssertionError("should raise exception") resp = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", self.params_premitive, From 07dadf7b65c2d55b7d941e891987c00a18ae9478 Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:05:22 +0800 Subject: [PATCH 4/8] reset test --- nebula3/gclient/net/base.py | 11 ++--------- tests/test_parameter.py | 3 +-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index f990d962..a3dedcf9 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -69,17 +69,10 @@ def execute_py( if not result.is_succeeded(): raise ExecuteError(stmt, params, result.error_code(), result.error_msg()) + if not primitive_res: return result - - full_result = [ - { - str(key): result.row_values(row_index)[i].cast_primitive() - for i, key in enumerate(result.keys()) - } - for row_index in range(result.row_size()) - ] - return full_result + return result.as_primitive() def _build_byte_param(params: dict) -> dict: diff --git a/tests/test_parameter.py b/tests/test_parameter.py index c57c446f..66a914f3 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -23,8 +23,7 @@ def setUp(self) -> None: self.configs = Config() self.configs.max_connection_pool_size = 6 self.pool = ConnectionPool() - # self.pool.init([("127.0.0.1", 9671)], self.configs) - self.pool.init([("127.0.0.1", 9669)], self.configs) + self.pool.init([("127.0.0.1", 9671)], self.configs) # get session from the pool client = self.pool.get_session("root", "nebula") From 311d2b544ae1d63c417d2b9f00fe9adccf90d5ed Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:06:23 +0800 Subject: [PATCH 5/8] format --- nebula3/gclient/net/base.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index a3dedcf9..524e0d7c 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -45,7 +45,8 @@ def execute_py( stmt: str, params: Optional[Dict[str, Any]] = None, primitive_res: Literal[True] = True, - ) -> List[Dict[str, Any]]: ... + ) -> List[Dict[str, Any]]: + ... @overload def execute_py( @@ -53,7 +54,8 @@ def execute_py( stmt: str, params: Optional[Dict[str, Any]] = None, primitive_res: Literal[False] = False, - ) -> ResultSet: ... + ) -> ResultSet: + ... def execute_py( self, From 9f28f9a36ad24861a7d95e11021a238b2db29bc0 Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:14:35 +0800 Subject: [PATCH 6/8] revert raw resultSet since python<=3.7 not support --- README.md | 5 ++--- example/Params.py | 5 ++--- nebula3/gclient/net/base.py | 21 --------------------- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index cbf01b49..d1fe5c34 100644 --- a/README.md +++ b/README.md @@ -137,14 +137,13 @@ params = { "ids": ["player100", "player101"], # second query } -resp: List[Dict[str, Any]] = client.execute_py( +resp = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", params, ) -resp: ResultSet = client.execute_py( +resp = client.execute_py( "MATCH (v) WHERE id(v) in $ids RETURN id(v) AS vertex_id", params, - primitive_res=False, ) ``` diff --git a/example/Params.py b/example/Params.py index 671fd0fe..ac3558c6 100644 --- a/example/Params.py +++ b/example/Params.py @@ -53,12 +53,11 @@ "p4": ["Bob", "Lily"], } -resp: List[Dict[str, Any]] = client.execute_py( +resp = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", params_premitive, ) -resp: ResultSet = client.execute_py( +resp = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", params_premitive, - primitive_res=False, ) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index 524e0d7c..37106afe 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -39,29 +39,10 @@ def execute(self, stmt: str) -> ResultSet: def execute_json(self, stmt: str) -> bytes: return self.execute_json_with_parameter(stmt, None) - @overload def execute_py( self, stmt: str, params: Optional[Dict[str, Any]] = None, - primitive_res: Literal[True] = True, - ) -> List[Dict[str, Any]]: - ... - - @overload - def execute_py( - self, - stmt: str, - params: Optional[Dict[str, Any]] = None, - primitive_res: Literal[False] = False, - ) -> ResultSet: - ... - - def execute_py( - self, - stmt: str, - params: Optional[Dict[str, Any]] = None, - primitive_res: bool = True, ): """**Recommended** Execute a statement with parameters in Python type instead of thrift type.""" if params is None: @@ -72,8 +53,6 @@ def execute_py( if not result.is_succeeded(): raise ExecuteError(stmt, params, result.error_code(), result.error_msg()) - if not primitive_res: - return result return result.as_primitive() From 65cedf1e64213bec27cb606170d689601b8a51d9 Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:17:03 +0800 Subject: [PATCH 7/8] again remove Literal --- nebula3/gclient/net/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index 37106afe..46fbe653 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -1,6 +1,6 @@ import datetime from abc import abstractmethod -from typing import Dict, Any, Optional, overload, Literal, List +from typing import Dict, Any, Optional from nebula3.data.ResultSet import ResultSet from nebula3.common.ttypes import ErrorCode, Value, NList, Date, Time, DateTime From cb1633a950096fa2b325f4ae8aa5f2c8a191e6ba Mon Sep 17 00:00:00 2001 From: Yanli Date: Fri, 31 May 2024 17:20:52 +0800 Subject: [PATCH 8/8] return raw resultSet --- nebula3/gclient/net/base.py | 2 +- tests/test_parameter.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nebula3/gclient/net/base.py b/nebula3/gclient/net/base.py index 46fbe653..71c4b9ba 100644 --- a/nebula3/gclient/net/base.py +++ b/nebula3/gclient/net/base.py @@ -53,7 +53,7 @@ def execute_py( if not result.is_succeeded(): raise ExecuteError(stmt, params, result.error_code(), result.error_msg()) - return result.as_primitive() + return result def _build_byte_param(params: dict) -> dict: diff --git a/tests/test_parameter.py b/tests/test_parameter.py index 66a914f3..609e5e95 100644 --- a/tests/test_parameter.py +++ b/tests/test_parameter.py @@ -144,7 +144,7 @@ def test_parameter(self): resp = client.execute_py( "RETURN abs($p1)+3 AS col1, (toBoolean($p2) and false) AS col2, toLower($p3)+1 AS col3", self.params_premitive, - ) + ).as_primitive() assert 1 == len(resp) assert ["col1", "col2", "col3"] == list(resp[0].keys()) assert resp[0]["col1"] == 6 @@ -171,7 +171,7 @@ def test_parameter(self): resp = client.execute_py( "MATCH (v) WHERE id(v) in $p4 RETURN id(v) AS vertex_id", self.params_premitive, - ) + ).as_primitive() assert 2 == len(resp) def tearDown(self) -> None: