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

jin pushed a commit to branch py-client-new
in repository https://gitbox.apache.org/repos/asf/incubator-hugegraph-ai.git


The following commit(s) were added to refs/heads/py-client-new by this push:
     new 5e0c268  feat(client): support graphspace & refactor the request logic 
(#61)
5e0c268 is described below

commit 5e0c26883d1a80293ae9838829977270b6cb8d1b
Author: PeiChaoXu <[email protected]>
AuthorDate: Fri Aug 9 11:25:43 2024 +0800

    feat(client): support graphspace & refactor the request logic (#61)
    
    Also we keep the compatible with the legacy request url/uri~
    
    This PR introduces significant updates to the hugegraph-python-client 
module, focusing on GraphSpace compatibility and API v3 support. Key changes 
include:
    
    - [x] Unified connection configuration via HGraphConfig to streamline 
parameter management.
    - [x] Enhanced HGraphSession for unified HTTP calls with optimized URL 
handling.
    - [x] Automatic GraphSpace detection based on server API version, ensuring 
compatibility with both old and new server versions.
    - [x] Refactored HGraphContext and HGraphRouter for more structured URL 
definitions using a decorator approach.
    - [x] Introduction of the @http decorator for simplified HTTP method and 
path definitions.
    - [ ] Extensive compatibility testing to ensure a seamless experience 
across different server versions.
    
    The update aims to provide a robust and flexible client module for 
interacting with HugeGraph servers, catering to the needs of both new and 
existing API versions.
    
    ---------
    
    Co-authored-by: imbajin <[email protected]>
---
 .../src/pyhugegraph/api/auth.py                    | 233 +++++---------------
 .../src/pyhugegraph/api/common.py                  |  32 +--
 .../src/pyhugegraph/api/graph.py                   | 206 ++++++------------
 .../src/pyhugegraph/api/graphs.py                  |  41 ++--
 .../src/pyhugegraph/api/gremlin.py                 |  33 ++-
 .../src/pyhugegraph/api/metric.py                  |  90 ++------
 .../src/pyhugegraph/api/schema.py                  |  65 +++---
 .../pyhugegraph/api/schema_manage/edge_label.py    |  58 ++---
 .../pyhugegraph/api/schema_manage/index_label.py   |  34 ++-
 .../pyhugegraph/api/schema_manage/property_key.py  |  69 +++---
 .../pyhugegraph/api/schema_manage/vertex_label.py  |  57 ++---
 .../src/pyhugegraph/api/task.py                    |  37 +---
 .../src/pyhugegraph/api/traverser.py               | 239 +++++++--------------
 .../src/pyhugegraph/api/variable.py                |  42 +---
 .../src/pyhugegraph/api/version.py                 |  15 +-
 hugegraph-python-client/src/pyhugegraph/client.py  | 132 +++++++-----
 .../src/pyhugegraph/example/hugegraph_example.py   |  18 +-
 .../src/pyhugegraph/example/hugegraph_test.py      |   4 +-
 .../src/pyhugegraph/structure/graph_instance.py    |  58 -----
 .../src/pyhugegraph/structure/property_key_data.py |  21 +-
 .../src/pyhugegraph/utils/huge_config.py           |  67 ++++++
 .../src/pyhugegraph/utils/huge_decorator.py        |   1 +
 .../src/pyhugegraph/utils/huge_requests.py         | 106 ++++++++-
 .../src/pyhugegraph/utils/huge_router.py           | 130 +++++++++++
 .../src/pyhugegraph/utils/log.py                   |  75 +++++++
 .../src/pyhugegraph/utils/util.py                  |  12 +-
 hugegraph-python-client/src/tests/api/test_auth.py |   8 +-
 27 files changed, 887 insertions(+), 996 deletions(-)

diff --git a/hugegraph-python-client/src/pyhugegraph/api/auth.py 
b/hugegraph-python-client/src/pyhugegraph/api/auth.py
index 0ce1103..09c80b0 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/auth.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/auth.py
@@ -20,67 +20,42 @@ import json
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class AuthManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", "auth/users")
     def list_users(self, limit=None):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/users"
-        params = {}
-        if limit is not None:
-            params["limit"] = limit
-        response = self.__session.get(
-            url,
-            params=params,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        params = {"limit": limit} if limit is not None else {}
+        response = self._invoke_request(params=params)
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return []
 
+    @router.http("POST", "auth/users")
     def create_user(self, user_name, user_password, user_phone=None, 
user_email=None):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/users"
         data = {
             "user_name": user_name,
             "user_password": user_password,
             "user_phone": user_phone,
             "user_email": user_email,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "auth/users/{user_id}")
     def delete_user(self, user_id):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/users/{user_id}"
-        response = self.__session.delete(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             if response.status_code != 204:
                 return response.json()
         return {}
 
+    @router.http("PUT", "auth/users/{user_id}")
     def modify_user(
         self,
         user_id,
@@ -89,275 +64,181 @@ class AuthManager(HugeParamsBase):
         user_phone=None,
         user_email=None,
     ):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/users/{user_id}"
         data = {
             "user_name": user_name,
             "user_password": user_password,
             "user_phone": user_phone,
             "user_email": user_email,
         }
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/users/{user_id}")
     def get_user(self, user_id):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/users/{user_id}"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/groups")
     def list_groups(self, limit=None):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/groups"
-        params = {}
-        if limit is not None:
-            params["limit"] = limit
-        response = self.__session.get(
-            url,
-            params=params,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        params = {"limit": limit} if limit is not None else {}
+        response = self._invoke_request(params=params)
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return []
 
+    @router.http("POST", "auth/groups")
     def create_group(self, group_name, group_description=None):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/groups"
         data = {"group_name": group_name, "group_description": 
group_description}
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "auth/groups/{group_id}")
     def delete_group(self, group_id):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/groups/{group_id}"
-        response = self.__session.delete(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             if response.status_code != 204:
                 return response.json()
         return {}
 
+    @router.http("PUT", "auth/groups/{group_id}")
     def modify_group(self, group_id, group_name=None, group_description=None):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/groups/{group_id}"
         data = {"group_name": group_name, "group_description": 
group_description}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/groups/{group_id}")
     def get_group(self, group_id):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/groups/{group_id}"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "auth/accesses")
     def grant_accesses(self, group_id, target_id, access_permission):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/accesses"
         data = {
             "group": group_id,
             "target": target_id,
             "access_permission": access_permission,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "auth/accesses/{access_id}")
     def revoke_accesses(self, access_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/accesses/{access_id}"
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
 
+    @router.http("PUT", "auth/accesses/{access_id}")
     def modify_accesses(self, access_id, access_description):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/accesses/{access_id}"
         # The permission of access can\'t be updated
         data = {"access_description": access_description}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/accesses/{access_id}")
     def get_accesses(self, access_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/accesses/{access_id}"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/accesses")
     def list_accesses(self):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/accesses"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "auth/targets")
     def create_target(self, target_name, target_graph, target_url, 
target_resources):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/targets"
         data = {
             "target_name": target_name,
             "target_graph": target_graph,
             "target_url": target_url,
             "target_resources": target_resources,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "auth/targets/{target_id}")
     def delete_target(self, target_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/targets/{target_id}"
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
 
-    def update_target(self, target_id, target_name, target_graph, target_url, 
target_resources):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/targets/{target_id}"
+    @router.http("PUT", "auth/targets/{target_id}")
+    def update_target(
+        self, target_id, target_name, target_graph, target_url, 
target_resources
+    ):
         data = {
             "target_name": target_name,
             "target_graph": target_graph,
             "target_url": target_url,
             "target_resources": target_resources,
         }
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
-    def get_target(self, target_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/targets/{target_id}"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+    @router.http("GET", "auth/targets/{target_id}")
+    def get_target(self, target_id, response=None):
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/targets")
     def list_targets(self):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/targets"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "auth/belongs")
     def create_belong(self, user_id, group_id):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/belongs"
         data = {"user": user_id, "group": group_id}
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "auth/belongs/{belong_id}")
     def delete_belong(self, belong_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/belongs/{belong_id}"
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
 
+    @router.http("PUT", "auth/belongs/{belong_id}")
     def update_belong(self, belong_id, description):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/belongs/{belong_id}"
         data = {"belong_description": description}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/belongs/{belong_id}")
     def get_belong(self, belong_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/auth/belongs/{belong_id}"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "auth/belongs")
     def list_belongs(self):
-        url = f"{self._host}/graphs/{self._graph_name}/auth/belongs"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
diff --git a/hugegraph-python-client/src/pyhugegraph/api/common.py 
b/hugegraph-python-client/src/pyhugegraph/api/common.py
index a1dd7d8..731d4d2 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/common.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/common.py
@@ -14,9 +14,14 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-from pyhugegraph.utils.constants import Constants
 
 
+from abc import ABC
+from pyhugegraph.utils.huge_router import HGraphRouter
+from pyhugegraph.utils.huge_requests import HGraphSession
+
+
+# todo: rename -> HGraphMetaData or delete
 class ParameterHolder:
     def __init__(self):
         self._dic = {}
@@ -36,19 +41,20 @@ class ParameterHolder:
         return self._dic.keys()
 
 
-class HugeParamsBase:
-    def __init__(self, graph_instance):
-        self._graph_instance = graph_instance
-        self._ip = graph_instance.ip
-        self._port = graph_instance.port
-        self._user = graph_instance.user_name
-        self._pwd = graph_instance.passwd
-        self._host = f"http://{graph_instance.ip}:{graph_instance.port}";
-        self._auth = (graph_instance.user_name, graph_instance.passwd)
-        self._graph_name = graph_instance.graph_name
+class HGraphContext(ABC):
+    def __init__(self, sess: HGraphSession) -> None:
+        self._sess = sess
+        self._cache = {}  # todo: move parameter_holder to cache
+
+    def close(self):
+        self._sess.close()
+
+
+# todo: rename -> HGraphModule | HGraphRouterable | HGraphModel
+class HugeParamsBase(HGraphContext, HGraphRouter):
+    def __init__(self, sess: HGraphSession) -> None:
+        super().__init__(sess)
         self._parameter_holder = None
-        self._headers = {"Content-Type": Constants.HEADER_CONTENT_TYPE}
-        self._timeout = graph_instance.timeout
 
     def add_parameter(self, key, value):
         self._parameter_holder.set(key, value)
diff --git a/hugegraph-python-client/src/pyhugegraph/api/graph.py 
b/hugegraph-python-client/src/pyhugegraph/api/graph.py
index 959e6b0..708d63c 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/graph.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/graph.py
@@ -17,10 +17,10 @@
 
 import json
 
-from pyhugegraph.utils.huge_requests import HugeSession
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.structure.vertex_data import VertexData
 from pyhugegraph.structure.edge_data import EdgeData
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.exceptions import (
     NotFoundError,
     CreateError,
@@ -35,28 +35,15 @@ from pyhugegraph.utils.util import (
 
 
 class GraphManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("POST", "graph/vertices")
     def addVertex(self, label, properties, id=None):
         data = {}
         if id is not None:
             data["id"] = id
         data["label"] = label
         data["properties"] = properties
-        url = f"{self._host}/graphs/{self._graph_name}/graph/vertices"
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(
             response, CreateError(f"create vertex failed: 
{str(response.content)}")
         ):
@@ -64,19 +51,12 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("POST", "graph/vertices/batch")
     def addVertices(self, input_data):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/vertices/batch"
-
         data = []
         for item in input_data:
             data.append({"label": item[0], "properties": item[1]})
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(
             response, CreateError(f"create vertexes failed: 
{str(response.content)}")
         ):
@@ -86,17 +66,10 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("PUT", 'graph/vertices/"{vertex_id}"?action=append')
     def appendVertex(self, vertex_id, properties):
-        url = 
f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"?action=append'
-
         data = {"properties": properties}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(
             response, UpdateError(f"append vertex failed: 
{str(response.content)}")
         ):
@@ -104,19 +77,10 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("PUT", 'graph/vertices/"{vertex_id}"?action=eliminate')
     def eliminateVertex(self, vertex_id, properties):
-        url = (
-            
f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"?action=eliminate'
-        )
-
         data = {"properties": properties}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(
             response, UpdateError(f"eliminate vertex failed: 
{str(response.content)}")
         ):
@@ -124,20 +88,18 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("GET", 'graph/vertices/"{vertex_id}"')
     def getVertexById(self, vertex_id):
-        url = 
f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"'
-
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, NotFoundError(f"Vertex not found: 
{str(response.content)}")):
+        response = self._invoke_request()
+        if check_if_success(
+            response, NotFoundError(f"Vertex not found: 
{str(response.content)}")
+        ):
             res = VertexData(json.loads(response.content))
             return res
         return None
 
     def getVertexByPage(self, label, limit, page=None, properties=None):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/vertices?"
-
+        path = "graph/vertices?"
         para = ""
         para = para + "&label=" + label
         if properties:
@@ -147,11 +109,11 @@ class GraphManager(HugeParamsBase):
         else:
             para += "&page"
         para = para + "&limit=" + str(limit)
-        url = url + para[1:]
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, NotFoundError(f"Vertex not found: 
{str(response.content)}")):
+        path = path + para[1:]
+        response = self._sess.request(path)
+        if check_if_success(
+            response, NotFoundError(f"Vertex not found: 
{str(response.content)}")
+        ):
             res = []
             for item in json.loads(response.content)["vertices"]:
                 res.append(VertexData(item))
@@ -160,8 +122,7 @@ class GraphManager(HugeParamsBase):
         return None
 
     def getVertexByCondition(self, label="", limit=0, page=None, 
properties=None):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/vertices?"
-
+        path = "graph/vertices?"
         para = ""
         if label:
             para = para + "&label=" + label
@@ -173,52 +134,44 @@ class GraphManager(HugeParamsBase):
             para += f"&page={page}"
         else:
             para += "&page"
-        url = url + para[1:]
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, NotFoundError(f"Vertex not found: 
{str(response.content)}")):
+        path = path + para[1:]
+        response = self._sess.request(path)
+        if check_if_success(
+            response, NotFoundError(f"Vertex not found: 
{str(response.content)}")
+        ):
             res = []
             for item in json.loads(response.content)["vertices"]:
                 res.append(VertexData(item))
             return res
         return None
 
+    @router.http("DELETE", 'graph/vertices/"{vertex_id}"')
     def removeVertexById(self, vertex_id):
-        url = 
f'{self._host}/graphs/{self._graph_name}/graph/vertices/"{vertex_id}"'
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(
             response, RemoveError(f"remove vertex failed: 
{str(response.content)}")
         ):
             return response.content
         return None
 
+    @router.http("POST", "graph/edges")
     def addEdge(self, edge_label, out_id, in_id, properties):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/edges"
-
         data = {
             "label": edge_label,
             "outV": out_id,
             "inV": in_id,
             "properties": properties,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
-        if check_if_success(response, CreateError(f"created edge failed: 
{str(response.content)}")):
+        response = self._invoke_request(data=json.dumps(data))
+        if check_if_success(
+            response, CreateError(f"created edge failed: 
{str(response.content)}")
+        ):
             res = EdgeData(json.loads(response.content))
             return res
         return None
 
+    @router.http("POST", "graph/edges/batch")
     def addEdges(self, input_data):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/edges/batch"
-
         data = []
         for item in input_data:
             data.append(
@@ -231,13 +184,7 @@ class GraphManager(HugeParamsBase):
                     "properties": item[5],
                 }
             )
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(
             response, CreateError(f"created edges failed:  
{str(response.content)}")
         ):
@@ -247,33 +194,19 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("PUT", "graph/edges/{edge_id}?action=append")
     def appendEdge(self, edge_id, properties):
-        url = 
f"{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}?action=append"
-
-        data = {"properties": properties}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
-        if check_if_success(response, UpdateError(f"append edge failed: 
{str(response.content)}")):
+        response = self._invoke_request(data=json.dumps({"properties": 
properties}))
+        if check_if_success(
+            response, UpdateError(f"append edge failed: 
{str(response.content)}")
+        ):
             res = EdgeData(json.loads(response.content))
             return res
         return None
 
+    @router.http("PUT", "graph/edges/{edge_id}?action=eliminate")
     def eliminateEdge(self, edge_id, properties):
-        url = 
f"{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}?action=eliminate"
-
-        data = {"properties": properties}
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps({"properties": 
properties}))
         if check_if_success(
             response, UpdateError(f"eliminate edge failed: 
{str(response.content)}")
         ):
@@ -281,13 +214,12 @@ class GraphManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("GET", "graph/edges/{edge_id}")
     def getEdgeById(self, edge_id):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}"
-
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, NotFoundError(f"not found edge: 
{str(response.content)}")):
+        response = self._invoke_request()
+        if check_if_success(
+            response, NotFoundError(f"not found edge: {str(response.content)}")
+        ):
             res = EdgeData(json.loads(response.content))
             return res
         return None
@@ -301,8 +233,7 @@ class GraphManager(HugeParamsBase):
         page=None,
         properties=None,
     ):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/edges?"
-
+        path = f"graph/edges?"
         para = ""
         if vertex_id:
             if direction:
@@ -319,37 +250,34 @@ class GraphManager(HugeParamsBase):
             para += "&page"
         if limit > 0:
             para = para + "&limit=" + str(limit)
-        url = url + para[1:]
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, NotFoundError(f"not found edges: 
{str(response.content)}")):
+        path = path + para[1:]
+        response = self._sess.request(path)
+        if check_if_success(
+            response, NotFoundError(f"not found edges: 
{str(response.content)}")
+        ):
             res = []
             for item in json.loads(response.content)["edges"]:
                 res.append(EdgeData(item))
             return res, json.loads(response.content)["page"]
         return None
 
+    @router.http("DELETE", "graph/edges/{edge_id}")
     def removeEdgeById(self, edge_id):
-        url = f"{self._host}/graphs/{self._graph_name}/graph/edges/{edge_id}"
-
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
-        if check_if_success(response, RemoveError(f"remove edge failed: 
{str(response.content)}")):
+        response = self._invoke_request()
+        if check_if_success(
+            response, RemoveError(f"remove edge failed: 
{str(response.content)}")
+        ):
             return response.content
         return None
 
     def getVerticesById(self, vertex_ids):
         if not vertex_ids:
             return []
-        url = f"{self._host}/graphs/{self._graph_name}/traversers/vertices?"
+        path = "traversers/vertices?"
         for vertex_id in vertex_ids:
-            url += f'ids="{vertex_id}"&'
-        url = url.rstrip("&")
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+            path += f'ids="{vertex_id}"&'
+        path = path.rstrip("&")
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             res = []
             for item in json.loads(response.content)["vertices"]:
@@ -361,13 +289,11 @@ class GraphManager(HugeParamsBase):
     def getEdgesById(self, edge_ids):
         if not edge_ids:
             return []
-        url = f"{self._host}/graphs/{self._graph_name}/traversers/edges?"
+        path = "traversers/edges?"
         for vertex_id in edge_ids:
-            url += f"ids={vertex_id}&"
-        url = url.rstrip("&")
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+            path += f"ids={vertex_id}&"
+        path = path.rstrip("&")
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             res = []
             for item in json.loads(response.content)["edges"]:
diff --git a/hugegraph-python-client/src/pyhugegraph/api/graphs.py 
b/hugegraph-python-client/src/pyhugegraph/api/graphs.py
index 8212a90..c7f4e30 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/graphs.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/graphs.py
@@ -15,58 +15,53 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import json
 
 from pyhugegraph.api.common import HugeParamsBase
-
-from pyhugegraph.utils.constants import Constants
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
 from pyhugegraph.utils.util import check_if_success
 
 
 class GraphsManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", "/graphs")
     def get_all_graphs(self):
-        url = f"{self._host}/graphs"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return str(response.content)
         return ""
 
+    @router.http("GET", "/versions")
     def get_version(self):
-        url = f"{self._host}/versions"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return str(response.content)
         return ""
 
+    @router.http("GET", "")
     def get_graph_info(self):
-        url = f"{self._host}/graphs/{self._graph_name}"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return str(response.content)
         return ""
 
     def clear_graph_all_data(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/clear?confirm_message="
-            f"{Constants.CONFORM_MESSAGE}"
-        )
-        response = self.__session.delete(url, auth=self._auth, 
headers=self._headers)
+        if self._sess._cfg.gs_supported:
+            response = self._sess.request(
+                "", "PUT", data=json.dumps({"action": "clear", "clear_schema": 
True})
+            )
+        else:
+            response = self._sess.request(
+                "clear?confirm_message=I%27m+sure+to+delete+all+data", "DELETE"
+            )
         if check_if_success(response, NotFoundError(response.content)):
             return str(response.content)
         return ""
 
+    @router.http("GET", "conf")
     def get_graph_config(self):
-        url = self._host + "/graphs" + "/" + self._graph_name + "/conf"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return str(response.content)
         return ""
diff --git a/hugegraph-python-client/src/pyhugegraph/api/gremlin.py 
b/hugegraph-python-client/src/pyhugegraph/api/gremlin.py
index 5726075..3095642 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/gremlin.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/gremlin.py
@@ -21,33 +21,26 @@ from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.structure.gremlin_data import GremlinData
 from pyhugegraph.structure.response_data import ResponseData
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class GremlinManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("POST", "/gremlin")
     def exec(self, gremlin):
-        url = f"{self._host}/gremlin"
         gremlin_data = GremlinData(gremlin)
-        gremlin_data.aliases = {
-            "graph": self._graph_name,
-            "g": "__g_" + self._graph_name,
-        }
-        response = self.__session.post(
-            url,
-            data=gremlin_data.to_json(),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        if self._sess._cfg.gs_supported:
+            gremlin_data.aliases = {
+                "graph": 
f"{self._sess._cfg.graphspace}-{self._sess._cfg.graph_name}",
+                "g": 
f"__g_{self._sess._cfg.graphspace}-{self._sess._cfg.graph_name}",
+            }
+        else:
+            gremlin_data.aliases = {
+                "graph": f"{self._sess._cfg.graph_name}",
+                "g": f"__g_{self._sess._cfg.graph_name}",
+            }
+        response = self._invoke_request(data=gremlin_data.to_json())
         error = NotFoundError(f"Gremlin can't get results: 
{str(response.content)}")
         if check_if_success(response, error):
             return ResponseData(json.loads(response.content)).result
diff --git a/hugegraph-python-client/src/pyhugegraph/api/metric.py 
b/hugegraph-python-client/src/pyhugegraph/api/metric.py
index 51be7b4..9d27d52 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/metric.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/metric.py
@@ -17,123 +17,71 @@
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class MetricsManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", "/metrics/?type=json")
     def get_all_basic_metrics(self):
-        url = f"{self._host}/metrics/?type=json"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/gauges")
     def get_gauges_metrics(self):
-        url = f"{self._host}/metrics/gauges"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/counters")
     def get_counters_metrics(self):
-        url = f"{self._host}/metrics/counters"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/gauges")
     def get_histograms_metrics(self):
-        url = f"{self._host}/metrics/histograms"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/meters")
     def get_meters_metrics(self):
-        url = f"{self._host}/metrics/meters"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/timers")
     def get_timers_metrics(self):
-        url = f"{self._host}/metrics/timers"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/statistics/?type=json")
     def get_statistics_metrics(self):
-        url = f"{self._host}/metrics/statistics/?type=json"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/system")
     def get_system_metrics(self):
-        url = f"{self._host}/metrics/system"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "/metrics/backend")
     def get_backend_metrics(self):
-        url = f"{self._host}/metrics/backend"
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
diff --git a/hugegraph-python-client/src/pyhugegraph/api/schema.py 
b/hugegraph-python-client/src/pyhugegraph/api/schema.py
index 2818c5b..ab92bc8 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/schema.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/schema.py
@@ -18,6 +18,7 @@
 
 import json
 
+from typing import Optional, Dict
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.api.schema_manage.edge_label import EdgeLabel
 from pyhugegraph.api.schema_manage.index_label import IndexLabel
@@ -28,32 +29,24 @@ from pyhugegraph.structure.index_label_data import 
IndexLabelData
 from pyhugegraph.structure.property_key_data import PropertyKeyData
 from pyhugegraph.structure.vertex_label_data import VertexLabelData
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
 from pyhugegraph.utils.util import check_if_success
+from pyhugegraph.utils import huge_router as router
 
 
 class SchemaManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
-
     """
     create schemas, including propertyKey/vertexLabel/edgeLabel/indexLabel
     """
 
-    def propertyKey(self, property_name):
-        property_key = PropertyKey(self._graph_instance, self.__session)
+    def propertyKey(self, property_name) -> PropertyKey:
+        property_key = PropertyKey(self._sess)
         property_key.create_parameter_holder()
         property_key.add_parameter("name", property_name)
         property_key.add_parameter("not_exist", True)
         return property_key
 
     def vertexLabel(self, vertex_name):
-        vertex_label = VertexLabel(self._graph_instance, self.__session)
+        vertex_label = VertexLabel(self._sess)
         vertex_label.create_parameter_holder()
         vertex_label.add_parameter("name", vertex_name)
         # vertex_label.add_parameter("id_strategy", "AUTOMATIC")
@@ -61,39 +54,39 @@ class SchemaManager(HugeParamsBase):
         return vertex_label
 
     def edgeLabel(self, name):
-        edge_label = EdgeLabel(self._graph_instance, self.__session)
+        edge_label = EdgeLabel(self._sess)
         edge_label.create_parameter_holder()
         edge_label.add_parameter("name", name)
         edge_label.add_parameter("not_exist", True)
         return edge_label
 
     def indexLabel(self, name):
-        index_label = IndexLabel(self._graph_instance, self.__session)
+        index_label = IndexLabel(self._sess)
         index_label.create_parameter_holder()
         index_label.add_parameter("name", name)
         return index_label
 
-    def getSchema(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    @router.http("GET", "schema?format={format}")
+    def getSchema(self, format: str = "json") -> Optional[Dict]:
+        response = self._invoke_request()
         error = NotFoundError(f"schema not found: {str(response.content)}")
         if check_if_success(response, error):
             schema = json.loads(response.content)
             return schema
         return None
 
+    @router.http("GET", "schema/propertykeys/{property_name}")
     def getPropertyKey(self, property_name):
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/propertykeys/{property_name}"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         error = NotFoundError(f"PropertyKey not found: 
{str(response.content)}")
         if check_if_success(response, error):
             property_keys_data = PropertyKeyData(json.loads(response.content))
             return property_keys_data
         return None
 
+    @router.http("GET", "schema/propertykeys")
     def getPropertyKeys(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema/propertykeys"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         res = []
         if check_if_success(response):
             for item in json.loads(response.content)["propertykeys"]:
@@ -101,63 +94,63 @@ class SchemaManager(HugeParamsBase):
             return res
         return None
 
+    @router.http("GET", "schema/vertexlabels/{name}")
     def getVertexLabel(self, name):
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         error = NotFoundError(f"VertexLabel not found: 
{str(response.content)}")
         if check_if_success(response, error):
             res = VertexLabelData(json.loads(response.content))
             return res
         return None
 
+    @router.http("GET", "schema/vertexlabels")
     def getVertexLabels(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         res = []
         if check_if_success(response):
             for item in json.loads(response.content)["vertexlabels"]:
                 res.append(VertexLabelData(item))
         return res
 
-    def getEdgeLabel(self, label_name):
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/edgelabels/{label_name}"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    @router.http("GET", "schema/edgelabels/{label_name}")
+    def getEdgeLabel(self, label_name: str):
+        response = self._invoke_request()
         error = NotFoundError(f"EdgeLabel not found: {str(response.content)}")
         if check_if_success(response, error):
             res = EdgeLabelData(json.loads(response.content))
             return res
         return None
 
+    @router.http("GET", "schema/edgelabels")
     def getEdgeLabels(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema/edgelabels"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         res = []
         if check_if_success(response):
             for item in json.loads(response.content)["edgelabels"]:
                 res.append(EdgeLabelData(item))
         return res
 
+    @router.http("GET", "schema/edgelabels")
     def getRelations(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema/edgelabels"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         res = []
         if check_if_success(response):
             for item in json.loads(response.content)["edgelabels"]:
                 res.append(EdgeLabelData(item).relations())
         return res
 
+    @router.http("GET", "schema/indexlabels/{name}")
     def getIndexLabel(self, name):
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/indexlabels/{name}"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         error = NotFoundError(f"EdgeLabel not found: {str(response.content)}")
         if check_if_success(response, error):
             res = IndexLabelData(json.loads(response.content))
             return res
         return None
 
+    @router.http("GET", "schema/indexlabels")
     def getIndexLabels(self):
-        url = f"{self._host}/graphs/{self._graph_name}/schema/indexlabels"
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+        response = self._invoke_request()
         res = []
         if check_if_success(response):
             for item in json.loads(response.content)["indexlabels"]:
diff --git 
a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/edge_label.py 
b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/edge_label.py
index f694a49..ee6b8c1 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/edge_label.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/edge_label.py
@@ -24,28 +24,25 @@ from pyhugegraph.utils.util import check_if_success, 
check_if_authorized
 
 
 class EdgeLabel(HugeParamsBase):
-    def __init__(self, graph_instance, session):
-        super().__init__(graph_instance)
-        self.__session = session
 
     @decorator_params
-    def link(self, source_label, target_label):
+    def link(self, source_label, target_label) -> "EdgeLabel":
         self._parameter_holder.set("source_label", source_label)
         self._parameter_holder.set("target_label", target_label)
         return self
 
     @decorator_params
-    def sourceLabel(self, source_label):
+    def sourceLabel(self, source_label) -> "EdgeLabel":
         self._parameter_holder.set("source_label", source_label)
         return self
 
     @decorator_params
-    def targetLabel(self, target_label):
+    def targetLabel(self, target_label) -> "EdgeLabel":
         self._parameter_holder.set("target_label", target_label)
         return self
 
     @decorator_params
-    def userdata(self, *args):
+    def userdata(self, *args) -> "EdgeLabel":
         if not self._parameter_holder.get_value("user_data"):
             self._parameter_holder.set("user_data", {})
         user_data = self._parameter_holder.get_value("user_data")
@@ -56,39 +53,35 @@ class EdgeLabel(HugeParamsBase):
         return self
 
     @decorator_params
-    def properties(self, *args):
+    def properties(self, *args) -> "EdgeLabel":
         self._parameter_holder.set("properties", list(args))
         return self
 
     @decorator_params
-    def singleTime(self):
+    def singleTime(self) -> "EdgeLabel":
         self._parameter_holder.set("frequency", "SINGLE")
         return self
 
     @decorator_params
-    def multiTimes(self):
+    def multiTimes(self) -> "EdgeLabel":
         self._parameter_holder.set("frequency", "MULTIPLE")
         return self
 
     @decorator_params
-    def sortKeys(self, *args):
+    def sortKeys(self, *args) -> "EdgeLabel":
         self._parameter_holder.set("sort_keys", list(args))
         return self
 
     @decorator_params
-    def nullableKeys(self, *args):
+    def nullableKeys(self, *args) -> "EdgeLabel":
         nullable_keys = set(args)
         self._parameter_holder.set("nullable_keys", list(nullable_keys))
         return self
 
     @decorator_params
-    def ifNotExist(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}"
-            f'/schema/edgelabels/{self._parameter_holder.get_value("name")}'
-        )
-
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    def ifNotExist(self) -> "EdgeLabel":
+        path = f'schema/edgelabels/{self._parameter_holder.get_value("name")}'
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             self._parameter_holder.set("not_exist", False)
         return self
@@ -111,10 +104,8 @@ class EdgeLabel(HugeParamsBase):
         for key in keys:
             if key in dic:
                 data[key] = dic[key]
-        url = f"{self._host}/graphs/{self._graph_name}/schema/edgelabels"
-        response = self.__session.post(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = "schema/edgelabels"
+        response = self._sess.request(path, "POST", data=json.dumps(data))
         self.clean_parameter_holder()
         error = CreateError(
             f'CreateError: "create EdgeLabel failed", Detail:  
"{str(response.content)}"'
@@ -125,11 +116,8 @@ class EdgeLabel(HugeParamsBase):
 
     @decorator_params
     def remove(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/edgelabels/"
-            f'{self._parameter_holder.get_value("name")}'
-        )
-        response = self.__session.delete(url, auth=self._auth, 
headers=self._headers)
+        path = f'schema/edgelabels/{self._parameter_holder.get_value("name")}'
+        response = self._sess.request(path, "DELETE")
         self.clean_parameter_holder()
         error = RemoveError(
             f'RemoveError: "remove EdgeLabel failed", Detail:  
"{str(response.content)}"'
@@ -147,12 +135,8 @@ class EdgeLabel(HugeParamsBase):
             if key in dic:
                 data[key] = dic[key]
 
-        url = (
-            
f'{self._host}/graphs/{self._graph_name}/schema/edgelabels/{data["name"]}?action=append'
-        )
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = f'schema/edgelabels/{data["name"]}?action=append'
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         self.clean_parameter_holder()
         error = UpdateError(
             f'UpdateError: "append EdgeLabel failed", Detail: 
"{str(response.content)}"'
@@ -169,11 +153,9 @@ class EdgeLabel(HugeParamsBase):
             if self._parameter_holder.get_value("user_data")
             else {}
         )
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/edgelabels/{name}?action=eliminate"
+        path = f"schema/edgelabels/{name}?action=eliminate"
         data = {"name": name, "user_data": user_data}
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         self.clean_parameter_holder()
         error = UpdateError(
             f'UpdateError: "eliminate EdgeLabel failed", Detail: 
"{str(response.content)}"'
diff --git 
a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/index_label.py 
b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/index_label.py
index 8eeb437..09ee8aa 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/index_label.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/index_label.py
@@ -25,24 +25,21 @@ from pyhugegraph.utils.util import check_if_authorized, 
check_if_success
 
 
 class IndexLabel(HugeParamsBase):
-    def __init__(self, graph_instance, session):
-        super().__init__(graph_instance)
-        self.__session = session
 
     @decorator_params
-    def onV(self, vertex_label):
+    def onV(self, vertex_label) -> "IndexLabel":
         self._parameter_holder.set("base_value", vertex_label)
         self._parameter_holder.set("base_type", "VERTEX_LABEL")
         return self
 
     @decorator_params
-    def onE(self, edge_label):
+    def onE(self, edge_label) -> "IndexLabel":
         self._parameter_holder.set("base_value", edge_label)
         self._parameter_holder.set("base_type", "EDGE_LABEL")
         return self
 
     @decorator_params
-    def by(self, *args):
+    def by(self, *args) -> "IndexLabel":
         if "fields" not in self._parameter_holder.get_keys():
             self._parameter_holder.set("fields", set())
         s = self._parameter_holder.get_value("fields")
@@ -51,27 +48,24 @@ class IndexLabel(HugeParamsBase):
         return self
 
     @decorator_params
-    def secondary(self):
+    def secondary(self) -> "IndexLabel":
         self._parameter_holder.set("index_type", "SECONDARY")
         return self
 
     @decorator_params
-    def range(self):
+    def range(self) -> "IndexLabel":
         self._parameter_holder.set("index_type", "RANGE")
         return self
 
     @decorator_params
-    def Search(self):
+    def Search(self) -> "IndexLabel":
         self._parameter_holder.set("index_type", "SEARCH")
         return self
 
     @decorator_params
-    def ifNotExist(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/indexlabels/"
-            f'{self._parameter_holder.get_value("name")}'
-        )
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    def ifNotExist(self) -> "IndexLabel":
+        path = f'schema/indexlabels/{self._parameter_holder.get_value("name")}'
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             self._parameter_holder.set("not_exist", False)
         return self
@@ -85,10 +79,8 @@ class IndexLabel(HugeParamsBase):
         data["base_value"] = dic["base_value"]
         data["index_type"] = dic["index_type"]
         data["fields"] = list(dic["fields"])
-        url = f"{self._host}/graphs/{self._graph_name}/schema/indexlabels"
-        response = self.__session.post(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = f"schema/indexlabels"
+        response = self._sess.request(path, "POST", data=json.dumps(data))
         self.clean_parameter_holder()
         error = CreateError(
             f'CreateError: "create IndexLabel failed", Detail 
"{str(response.content)}"'
@@ -100,8 +92,8 @@ class IndexLabel(HugeParamsBase):
     @decorator_params
     def remove(self):
         name = self._parameter_holder.get_value("name")
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/indexlabels/{name}"
-        response = self.__session.delete(url, auth=self._auth, 
headers=self._headers)
+        path = f"schema/indexlabels/{name}"
+        response = self._sess.request(path, "DELETE")
         self.clean_parameter_holder()
         error = RemoveError(
             f'RemoveError: "remove IndexLabel failed", Detail 
"{str(response.content)}"'
diff --git 
a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/property_key.py 
b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/property_key.py
index e9f9e11..e6a1765 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/property_key.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/property_key.py
@@ -25,67 +25,69 @@ from pyhugegraph.utils.util import check_if_success, 
check_if_authorized
 
 
 class PropertyKey(HugeParamsBase):
-    def __init__(self, graph_instance, session):
-        super().__init__(graph_instance)
-        self.__session = session
 
     @decorator_params
-    def asInt(self):
+    def asInt(self) -> "PropertyKey":
         self._parameter_holder.set("data_type", "INT")
         return self
 
     @decorator_params
-    def asText(self):
+    def asLong(self) -> "PropertyKey":
+        self._parameter_holder.set("data_type", "LONG")
+        return self
+
+    @decorator_params
+    def asText(self) -> "PropertyKey":
         self._parameter_holder.set("data_type", "TEXT")
         return self
 
     @decorator_params
-    def asDouble(self):
+    def asDouble(self) -> "PropertyKey":
         self._parameter_holder.set("data_type", "DOUBLE")
         return self
 
     @decorator_params
-    def asDate(self):
+    def asDate(self) -> "PropertyKey":
         self._parameter_holder.set("data_type", "DATE")
         return self
 
     @decorator_params
-    def valueSingle(self):
+    def valueSingle(self) -> "PropertyKey":
         self._parameter_holder.set("cardinality", "SINGLE")
         return self
 
     @decorator_params
-    def valueList(self):
+    def valueList(self) -> "PropertyKey":
         self._parameter_holder.set("cardinality", "LIST")
         return self
 
     @decorator_params
-    def valueSet(self):
+    def valueSet(self) -> "PropertyKey":
         self._parameter_holder.set("cardinality", "SET")
         return self
 
     @decorator_params
-    def calcMax(self):
+    def calcMax(self) -> "PropertyKey":
         self._parameter_holder.set("aggregate_type", "MAX")
         return self
 
     @decorator_params
-    def calcMin(self):
+    def calcMin(self) -> "PropertyKey":
         self._parameter_holder.set("aggregate_type", "MIN")
         return self
 
     @decorator_params
-    def calcSum(self):
+    def calcSum(self) -> "PropertyKey":
         self._parameter_holder.set("aggregate_type", "SUM")
         return self
 
     @decorator_params
-    def calcOld(self):
+    def calcOld(self) -> "PropertyKey":
         self._parameter_holder.set("aggregate_type", "OLD")
         return self
 
     @decorator_params
-    def userdata(self, *args):
+    def userdata(self, *args) -> "PropertyKey":
         user_data = self._parameter_holder.get_value("user_data")
         if not user_data:
             self._parameter_holder.set("user_data", {})
@@ -96,12 +98,9 @@ class PropertyKey(HugeParamsBase):
             i += 2
         return self
 
-    def ifNotExist(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/propertykeys/"
-            f'{self._parameter_holder.get_value("name")}'
-        )
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    def ifNotExist(self) -> "PropertyKey":
+        path = 
f'schema/propertykeys/{self._parameter_holder.get_value("name")}'
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             self._parameter_holder.set("not_exist", False)
         return self
@@ -114,10 +113,8 @@ class PropertyKey(HugeParamsBase):
             property_keys["data_type"] = dic["data_type"]
         if "cardinality" in dic:
             property_keys["cardinality"] = dic["cardinality"]
-        url = f"{self._host}/graphs/{self._graph_name}/schema/propertykeys"
-        response = self.__session.post(
-            url, data=json.dumps(property_keys), auth=self._auth, 
headers=self._headers
-        )
+        path = "schema/propertykeys"
+        response = self._sess.request(path, "POST", 
data=json.dumps(property_keys))
         self.clean_parameter_holder()
         if check_if_success(
             response,
@@ -136,13 +133,8 @@ class PropertyKey(HugeParamsBase):
             user_data = {}
         data = {"name": property_name, "user_data": user_data}
 
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/propertykeys/"
-            f"{property_name}/?action=append"
-        )
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = f"schema/propertykeys/{property_name}/?action=append"
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         self.clean_parameter_holder()
         if check_if_success(
             response,
@@ -161,13 +153,8 @@ class PropertyKey(HugeParamsBase):
             user_data = {}
         data = {"name": property_name, "user_data": user_data}
 
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/propertykeys/"
-            f"{property_name}/?action=eliminate"
-        )
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = f"schema/propertykeys/{property_name}/?action=eliminate"
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         self.clean_parameter_holder()
         error = UpdateError(
             f'UpdateError: "eliminate PropertyKey failed", Detail: 
{str(response.content)}'
@@ -179,8 +166,8 @@ class PropertyKey(HugeParamsBase):
     @decorator_params
     def remove(self):
         dic = self._parameter_holder.get_dic()
-        url = 
f'{self._host}/graphs/{self._graph_name}/schema/propertykeys/{dic["name"]}'
-        response = self.__session.delete(url, auth=self._auth, 
headers=self._headers)
+        path = f'schema/propertykeys/{dic["name"]}'
+        response = self._sess.request(path, "DELETE")
         self.clean_parameter_holder()
         if check_if_success(
             response,
diff --git 
a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/vertex_label.py 
b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/vertex_label.py
index 0274ad6..a9b49e8 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/schema_manage/vertex_label.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/schema_manage/vertex_label.py
@@ -24,52 +24,49 @@ from pyhugegraph.utils.util import check_if_success, 
check_if_authorized
 
 
 class VertexLabel(HugeParamsBase):
-    def __init__(self, graph_instance, session):
-        super().__init__(graph_instance)
-        self.__session = session
 
     @decorator_params
-    def useAutomaticId(self):
+    def useAutomaticId(self) -> "VertexLabel":
         self._parameter_holder.set("id_strategy", "AUTOMATIC")
         return self
 
     @decorator_params
-    def useCustomizeStringId(self):
+    def useCustomizeStringId(self) -> "VertexLabel":
         self._parameter_holder.set("id_strategy", "CUSTOMIZE_STRING")
         return self
 
     @decorator_params
-    def useCustomizeNumberId(self):
+    def useCustomizeNumberId(self) -> "VertexLabel":
         self._parameter_holder.set("id_strategy", "CUSTOMIZE_NUMBER")
         return self
 
     @decorator_params
-    def usePrimaryKeyId(self):
+    def usePrimaryKeyId(self) -> "VertexLabel":
         self._parameter_holder.set("id_strategy", "PRIMARY_KEY")
         return self
 
     @decorator_params
-    def properties(self, *args):
+    def properties(self, *args) -> "VertexLabel":
         self._parameter_holder.set("properties", list(args))
         return self
 
     @decorator_params
-    def primaryKeys(self, *args):
+    def primaryKeys(self, *args) -> "VertexLabel":
         self._parameter_holder.set("primary_keys", list(args))
         return self
 
     @decorator_params
-    def nullableKeys(self, *args):
+    def nullableKeys(self, *args) -> "VertexLabel":
         self._parameter_holder.set("nullable_keys", list(args))
         return self
 
     @decorator_params
-    def enableLabelIndex(self, flag):
+    def enableLabelIndex(self, flag) -> "VertexLabel":
         self._parameter_holder.set("enable_label_index", flag)
         return self
 
     @decorator_params
-    def userdata(self, *args):
+    def userdata(self, *args) -> "VertexLabel":
         if "user_data" not in self._parameter_holder.get_keys():
             self._parameter_holder.set("user_data", {})
         user_data = self._parameter_holder.get_value("user_data")
@@ -79,13 +76,9 @@ class VertexLabel(HugeParamsBase):
             i += 2
         return self
 
-    def ifNotExist(self):
-        url = (
-            f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels/"
-            f'{self._parameter_holder.get_value("name")}'
-        )
-
-        response = self.__session.get(url, auth=self._auth, 
headers=self._headers)
+    def ifNotExist(self) -> "VertexLabel":
+        path = 
f'schema/vertexlabels/{self._parameter_holder.get_value("name")}'
+        response = self._sess.request(path)
         if response.status_code == 200 and check_if_authorized(response):
             self._parameter_holder.set("not_exist", False)
         return self
@@ -107,10 +100,8 @@ class VertexLabel(HugeParamsBase):
         for key in key_list:
             if key in dic:
                 data[key] = dic[key]
-        url = f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels"
-        response = self.__session.post(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        path = f"schema/vertexlabels"
+        response = self._sess.request(path, "POST", data=json.dumps(data))
         self.clean_parameter_holder()
         error = CreateError(
             f'CreateError: "create VertexLabel failed", Detail: 
"{str(response.content)}"'
@@ -125,20 +116,14 @@ class VertexLabel(HugeParamsBase):
         properties = dic["properties"] if "properties" in dic else []
         nullable_keys = dic["nullable_keys"] if "nullable_keys" in dic else []
         user_data = dic["user_data"] if "user_data" in dic else {}
-        url = (
-            f"{self._host}/graphs/{self._graph_name}"
-            f'/schema/vertexlabels/{dic["name"]}?action=append'
-        )
-
+        path = f'schema/vertexlabels/{dic["name"]}?action=append'
         data = {
             "name": dic["name"],
             "properties": properties,
             "nullable_keys": nullable_keys,
             "user_data": user_data,
         }
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         self.clean_parameter_holder()
         error = UpdateError(
             f'UpdateError: "append VertexLabel failed", Detail: 
"{str(response.content)}"'
@@ -150,8 +135,8 @@ class VertexLabel(HugeParamsBase):
     @decorator_params
     def remove(self):
         name = self._parameter_holder.get_value("name")
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}"
-        response = self.__session.delete(url, auth=self._auth, 
headers=self._headers)
+        path = f"schema/vertexlabels/{name}"
+        response = self._sess.request(path, "DELETE")
         self.clean_parameter_holder()
         error = RemoveError(
             f'RemoveError: "remove VertexLabel failed", Detail: 
"{str(response.content)}"'
@@ -163,7 +148,7 @@ class VertexLabel(HugeParamsBase):
     @decorator_params
     def eliminate(self):
         name = self._parameter_holder.get_value("name")
-        url = 
f"{self._host}/graphs/{self._graph_name}/schema/vertexlabels/{name}/?action=eliminate"
+        path = f"schema/vertexlabels/{name}/?action=eliminate"
 
         dic = self._parameter_holder.get_dic()
         user_data = dic["user_data"] if "user_data" in dic else {}
@@ -171,9 +156,7 @@ class VertexLabel(HugeParamsBase):
             "name": self._parameter_holder.get_value("name"),
             "user_data": user_data,
         }
-        response = self.__session.put(
-            url, data=json.dumps(data), auth=self._auth, headers=self._headers
-        )
+        response = self._sess.request(path, "PUT", data=json.dumps(data))
         error = UpdateError(
             f'UpdateError: "eliminate VertexLabel failed", Detail: 
"{str(response.content)}"'
         )
diff --git a/hugegraph-python-client/src/pyhugegraph/api/task.py 
b/hugegraph-python-client/src/pyhugegraph/api/task.py
index fa72540..c31eb8d 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/task.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/task.py
@@ -17,56 +17,37 @@
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class TaskManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", "tasks")
     def list_tasks(self, status=None, limit=None):
-        url = f"{self._host}/graphs/{self._graph_name}/tasks"
         params = {}
         if status is not None:
             params["status"] = status
         if limit is not None:
             params["limit"] = limit
-        response = self.__session.get(
-            url,
-            params=params,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(params=params)
         check_if_success(response, NotFoundError(response.content))
         return response.json()
 
+    @router.http("GET", "tasks/{task_id}")
     def get_task(self, task_id):
-        url = f"{self._host}/graphs/{self._graph_name}/tasks/{task_id}"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
         return response.json()
 
+    @router.http("DELETE", "tasks/{task_id}")
     def delete_task(self, task_id):
-        url = f"{self._host}/graphs/{self._graph_name}/tasks/{task_id}"
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
         return response.status_code
 
+    @router.http("PUT", "tasks/{task_id}?action=cancel")
     def cancel_task(self, task_id):
-        url = 
f"{self._host}/graphs/{self._graph_name}/tasks/{task_id}?action=cancel"
-        response = self.__session.put(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
         return response.json()
diff --git a/hugegraph-python-client/src/pyhugegraph/api/traverser.py 
b/hugegraph-python-client/src/pyhugegraph/api/traverser.py
index b88ea74..165178b 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/traverser.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/traverser.py
@@ -18,125 +18,87 @@ import json
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class TraverserManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.url = f"{self._host}/graphs/{self._graph_name}/traversers"
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", 
'traversers/kout?source="{source_id}"&max_depth={max_depth}')
     def k_out(self, source_id, max_depth):
-        url = f'{self.url}/kout?source="{source_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET", 
'traversers/kneighbor?source="{source_id}"&max_depth={max_depth}'
+    )
     def k_neighbor(self, source_id, max_depth):
-        url = 
f'{self.url}/kneighbor?source="{source_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET", 
'traversers/sameneighbors?vertex="{vertex_id}"&other="{other_id}"'
+    )
     def same_neighbors(self, vertex_id, other_id):
-        url = 
f'{self.url}/sameneighbors?vertex="{vertex_id}"&other="{other_id}"'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET", 
'traversers/jaccardsimilarity?vertex="{vertex_id}"&other="{other_id}"'
+    )
     def jaccard_similarity(self, vertex_id, other_id):
-        url = 
f'{self.url}/jaccardsimilarity?vertex="{vertex_id}"&other="{other_id}"'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/shortestpath?source="{source_id}"&target="{target_id}"&max_depth={max_depth}',
+    )
     def shortest_path(self, source_id, target_id, max_depth):
-        url = (
-            f"{self.url}/shortestpath?"
-            f'source="{source_id}"&target="{target_id}"&max_depth={max_depth}'
-        )
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/allshortestpaths?source="{source_id}"&target="{target_id}"&max_depth={max_depth}',
+    )
     def all_shortest_paths(self, source_id, target_id, max_depth):
-        url = (
-            f"{self.url}/allshortestpaths?"
-            f'source="{source_id}"&target="{target_id}"&max_depth={max_depth}'
-        )
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/weightedshortestpath?source="{source_id}"&target="{target_id}"&weight={weight}&max_depth={max_depth}',
+    )
     def weighted_shortest_path(self, source_id, target_id, weight, max_depth):
-        url = (
-            f"{self.url}/weightedshortestpath?"
-            
f'source="{source_id}"&target="{target_id}"&weight={weight}&max_depth={max_depth}'
-        )
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/singlesourceshortestpath?source="{source_id}"&max_depth={max_depth}',
+    )
     def single_source_shortest_path(self, source_id, max_depth):
-        url = 
f'{self.url}/singlesourceshortestpath?source="{source_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "traversers/multinodeshortestpath")
     def multi_node_shortest_path(
         self,
         vertices,
@@ -148,7 +110,6 @@ class TraverserManager(HugeParamsBase):
     ):
         if properties is None:
             properties = {}
-        url = f"{self.url}/multinodeshortestpath"
         data = {
             "vertices": {"ids": vertices},
             "step": {"direction": direction, "properties": properties},
@@ -156,34 +117,25 @@ class TraverserManager(HugeParamsBase):
             "capacity": capacity,
             "with_vertex": with_vertex,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/paths?source="{source_id}"&target="{target_id}"&max_depth={max_depth}',
+    )
     def paths(self, source_id, target_id, max_depth):
-        url = 
f'{self.url}/paths?source="{source_id}"&target="{target_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "traversers/customizedpaths")
     def customized_paths(
         self, sources, steps, sort_by="INCR", with_vertex=True, capacity=-1, 
limit=-1
     ):
-        url = f"{self.url}/customizedpaths"
-
         data = {
             "sources": sources,
             "steps": steps,
@@ -192,19 +144,15 @@ class TraverserManager(HugeParamsBase):
             "capacity": capacity,
             "limit": limit,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
-    def template_paths(self, sources, targets, steps, capacity=10000, 
limit=10, with_vertex=True):
-        url = f"{self.url}/templatepaths"
+    @router.http("POST", "traversers/templatepaths")
+    def template_paths(
+        self, sources, targets, steps, capacity=10000, limit=10, 
with_vertex=True
+    ):
         data = {
             "sources": sources,
             "targets": targets,
@@ -213,36 +161,31 @@ class TraverserManager(HugeParamsBase):
             "limit": limit,
             "with_vertex": with_vertex,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http(
+        "GET",
+        
'traversers/crosspoints?source="{source_id}"&target="{target_id}"&max_depth={max_depth}',
+    )
     def crosspoints(self, source_id, target_id, max_depth):
-        url = (
-            f"{self.url}/crosspoints?"
-            f'source="{source_id}"&target="{target_id}"&max_depth={max_depth}'
-        )
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "traversers/customizedcrosspoints")
     def customized_crosspoints(
-        self, sources, path_patterns, with_path=True, with_vertex=True, 
capacity=-1, limit=-1
+        self,
+        sources,
+        path_patterns,
+        with_path=True,
+        with_vertex=True,
+        capacity=-1,
+        limit=-1,
     ):
-        url = f"{self.url}/customizedcrosspoints"
         data = {
             "sources": sources,
             "path_patterns": path_patterns,
@@ -251,41 +194,26 @@ class TraverserManager(HugeParamsBase):
             "capacity": capacity,
             "limit": limit,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", 
'traversers/rings?source="{source_id}"&max_depth={max_depth}')
     def rings(self, source_id, max_depth):
-        url = f'{self.url}/rings?source="{source_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", 
'traversers/rays?source="{source_id}"&max_depth={max_depth}')
     def rays(self, source_id, max_depth):
-        url = f'{self.url}/rays?source="{source_id}"&max_depth={max_depth}'
-        response = self.__session.get(
-            url,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("POST", "traversers/fusiformsimilarity")
     def fusiform_similarity(
         self,
         sources,
@@ -303,7 +231,6 @@ class TraverserManager(HugeParamsBase):
         with_intermediary=False,
         with_vertex=True,
     ):
-        url = f"{self.url}/fusiformsimilarity"
         data = {
             "sources": sources,
             "label": label,
@@ -320,41 +247,23 @@ class TraverserManager(HugeParamsBase):
             "with_intermediary": with_intermediary,
             "with_vertex": with_vertex,
         }
-        response = self.__session.post(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps(data))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "traversers/vertices")
     def vertices(self, ids):
-        url = f"{self.url}/vertices"
-        params = {"ids": '"' + ids + '"'}
-        response = self.__session.get(
-            url,
-            params=params,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        params = {"ids": f'"{ids}"'}
+        response = self._invoke_request(params=params)
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "traversers/edges")
     def edges(self, ids):
-        url = f"{self.url}/edges"
         params = {"ids": ids}
-        response = self.__session.get(
-            url,
-            params=params,
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(params=params)
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
diff --git a/hugegraph-python-client/src/pyhugegraph/api/variable.py 
b/hugegraph-python-client/src/pyhugegraph/api/variable.py
index 2345bcd..4c060b9 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/variable.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/variable.py
@@ -19,58 +19,34 @@ import json
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class VariableManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("PUT", "variables/{key}")
     def set(self, key, value):
-        url = f"{self._host}/graphs/{self._graph_name}/variables/{key}"
-        data = {"data": value}
-
-        response = self.__session.put(
-            url,
-            data=json.dumps(data),
-            auth=self._auth,
-            headers=self._headers,
-            timeout=self._timeout,
-        )
+        response = self._invoke_request(data=json.dumps({"data": value}))
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "variables/{key}")
     def get(self, key):
-        url = f"{self._host}/graphs/{self._graph_name}/variables/{key}"
-
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("GET", "variables")
     def all(self):
-        url = f"{self._host}/graphs/{self._graph_name}/variables"
-
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
 
+    @router.http("DELETE", "variables/{key}")
     def remove(self, key):
-        url = f"{self._host}/graphs/{self._graph_name}/variables/{key}"
-
-        response = self.__session.delete(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         check_if_success(response, NotFoundError(response.content))
diff --git a/hugegraph-python-client/src/pyhugegraph/api/version.py 
b/hugegraph-python-client/src/pyhugegraph/api/version.py
index 67fc5a5..23b5baf 100644
--- a/hugegraph-python-client/src/pyhugegraph/api/version.py
+++ b/hugegraph-python-client/src/pyhugegraph/api/version.py
@@ -17,24 +17,15 @@
 
 from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.utils.exceptions import NotFoundError
-from pyhugegraph.utils.huge_requests import HugeSession
+from pyhugegraph.utils import huge_router as router
 from pyhugegraph.utils.util import check_if_success
 
 
 class VersionManager(HugeParamsBase):
-    def __init__(self, graph_instance):
-        super().__init__(graph_instance)
-        self.__session = HugeSession.new_session()
-
-    def close(self):
-        if self.__session:
-            self.__session.close()
 
+    @router.http("GET", "/versions")
     def version(self):
-        url = f"{self._host}/versions"
-        response = self.__session.get(
-            url, auth=self._auth, headers=self._headers, timeout=self._timeout
-        )
+        response = self._invoke_request()
         if check_if_success(response, NotFoundError(response.content)):
             return response.json()
         return {}
diff --git a/hugegraph-python-client/src/pyhugegraph/client.py 
b/hugegraph-python-client/src/pyhugegraph/client.py
index b833415..fd776ff 100644
--- a/hugegraph-python-client/src/pyhugegraph/client.py
+++ b/hugegraph-python-client/src/pyhugegraph/client.py
@@ -14,8 +14,10 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
+
+from typing import Optional
+
 from pyhugegraph.api.auth import AuthManager
-from pyhugegraph.api.common import HugeParamsBase
 from pyhugegraph.api.graph import GraphManager
 from pyhugegraph.api.graphs import GraphsManager
 from pyhugegraph.api.gremlin import GremlinManager
@@ -25,60 +27,74 @@ from pyhugegraph.api.task import TaskManager
 from pyhugegraph.api.traverser import TraverserManager
 from pyhugegraph.api.variable import VariableManager
 from pyhugegraph.api.version import VersionManager
-from pyhugegraph.structure.graph_instance import GraphInstance
-
-
-class PyHugeClient(HugeParamsBase):
-    def __init__(self, ip, port, graph, user, pwd, timeout=10):
-        self._graph_instance = GraphInstance(ip, port, graph, user, pwd, 
timeout)
-        super().__init__(self._graph_instance)
-        self._schema = None
-        self._graph = None
-        self._graphs = None
-        self._gremlin = None
-        self._variable = None
-        self._auth = None
-        self._task = None
-        self._metrics = None
-        self._traverser = None
-        self._version = None
-
-    def schema(self):
-        self._schema = self._schema or SchemaManager(self._graph_instance)
-        return self._schema
-
-    def gremlin(self):
-        self._gremlin = self._gremlin or GremlinManager(self._graph_instance)
-        return self._gremlin
-
-    def graph(self):
-        self._graph = self._graph or GraphManager(self._graph_instance)
-        return self._graph
-
-    def graphs(self):
-        self._graphs = self._graphs or GraphsManager(self._graph_instance)
-        return self._graphs
-
-    def variable(self):
-        self._variable = self._variable or 
VariableManager(self._graph_instance)
-        return self._variable
-
-    def auth(self):
-        self._auth = self._auth or AuthManager(self._graph_instance)
-        return self._auth
-
-    def task(self):
-        self._task = self._task or TaskManager(self._graph_instance)
-        return self._task
-
-    def metrics(self):
-        self._metrics = self._metrics or MetricsManager(self._graph_instance)
-        return self._metrics
-
-    def traverser(self):
-        self._traverser = self._traverser or 
TraverserManager(self._graph_instance)
-        return self._traverser
-
-    def version(self):
-        self._version = self._version or VersionManager(self._graph_instance)
-        return self._version
+from pyhugegraph.utils.huge_config import HGraphConfig
+from pyhugegraph.utils.huge_requests import HGraphSession
+
+
+def manager_builder(fn):
+    attr_name = "_lazy_" + fn.__name__
+
+    def wrapper(self: "PyHugeClient"):
+        if not hasattr(self, attr_name):
+            session = HGraphSession(self._cfg)
+            setattr(self, attr_name, fn(self)(session))
+        return getattr(self, attr_name)
+
+    return wrapper
+
+
+class PyHugeClient:
+    def __init__(
+        self,
+        ip: str,
+        port: str,
+        graph: str,
+        user: str,
+        pwd: str,
+        timeout: int = 10,
+        gs: Optional[str] = None,
+    ):
+        self._cfg = HGraphConfig(ip, port, user, pwd, graph, gs, timeout)
+
+    @manager_builder
+    def schema(self) -> "SchemaManager":
+        return SchemaManager
+
+    @manager_builder
+    def gremlin(self) -> "GremlinManager":
+        return GremlinManager
+
+    @manager_builder
+    def graph(self) -> "GraphManager":
+        return GraphManager
+
+    @manager_builder
+    def graphs(self) -> "GraphsManager":
+        return GraphsManager
+
+    @manager_builder
+    def variable(self) -> "VariableManager":
+        return VariableManager
+
+    @manager_builder
+    def auth(self) -> "AuthManager":
+        return AuthManager
+
+    @manager_builder
+    def task(self) -> "TaskManager":
+        return TaskManager
+
+    @manager_builder
+    def metrics(self) -> "MetricsManager":
+        return MetricsManager
+
+    @manager_builder
+    def traverser(self) -> "TraverserManager":
+        return TraverserManager
+
+    @manager_builder
+    def version(self) -> "VersionManager":
+        return VersionManager
+
+    def __repr__(self) -> str:
+        return f"{self._cfg}"
diff --git 
a/hugegraph-python-client/src/pyhugegraph/example/hugegraph_example.py 
b/hugegraph-python-client/src/pyhugegraph/example/hugegraph_example.py
index 27d0246..d58190e 100644
--- a/hugegraph-python-client/src/pyhugegraph/example/hugegraph_example.py
+++ b/hugegraph-python-client/src/pyhugegraph/example/hugegraph_example.py
@@ -18,19 +18,23 @@
 from pyhugegraph.client import PyHugeClient
 
 if __name__ == "__main__":
-    client = PyHugeClient("127.0.0.1", "8080", user="admin", pwd="admin", 
graph="hugegraph")
+    client = PyHugeClient(
+        "127.0.0.1", "8080", user="admin", pwd="admin", graph="hugegraph"
+    )
 
     """schema"""
     schema = client.schema()
     schema.propertyKey("name").asText().ifNotExist().create()
     schema.propertyKey("birthDate").asText().ifNotExist().create()
-    schema.vertexLabel("Person").properties("name", 
"birthDate").usePrimaryKeyId().primaryKeys(
-        "name"
-    ).ifNotExist().create()
+    schema.vertexLabel("Person").properties(
+        "name", "birthDate"
+    ).usePrimaryKeyId().primaryKeys("name").ifNotExist().create()
     
schema.vertexLabel("Movie").properties("name").usePrimaryKeyId().primaryKeys(
         "name"
     ).ifNotExist().create()
-    
schema.edgeLabel("ActedIn").sourceLabel("Person").targetLabel("Movie").ifNotExist().create()
+    schema.edgeLabel("ActedIn").sourceLabel("Person").targetLabel(
+        "Movie"
+    ).ifNotExist().create()
 
     print(schema.getVertexLabels())
     print(schema.getEdgeLabels())
@@ -43,7 +47,9 @@ if __name__ == "__main__":
     p2 = g.addVertex("Person", {"name": "Robert De Niro", "birthDate": 
"1943-08-17"})
     m1 = g.addVertex("Movie", {"name": "The Godfather"})
     m2 = g.addVertex("Movie", {"name": "The Godfather Part II"})
-    m3 = g.addVertex("Movie", {"name": "The Godfather Coda The Death of 
Michael Corleone"})
+    m3 = g.addVertex(
+        "Movie", {"name": "The Godfather Coda The Death of Michael Corleone"}
+    )
 
     # add Edge
     g.addEdge("ActedIn", p1.id, m1.id, {})
diff --git a/hugegraph-python-client/src/pyhugegraph/example/hugegraph_test.py 
b/hugegraph-python-client/src/pyhugegraph/example/hugegraph_test.py
index 8b47f51..739862d 100644
--- a/hugegraph-python-client/src/pyhugegraph/example/hugegraph_test.py
+++ b/hugegraph-python-client/src/pyhugegraph/example/hugegraph_test.py
@@ -41,7 +41,9 @@ class HugeGraph:
         self.address = address
         self.port = port
         self.graph = graph
-        self.client = PyHugeClient(address, port, user=username, pwd=password, 
graph=graph)
+        self.client = PyHugeClient(
+            address, port, user=username, pwd=password, graph=graph
+        )
         self.schema = ""
 
     def exec(self, query) -> str:
diff --git 
a/hugegraph-python-client/src/pyhugegraph/structure/graph_instance.py 
b/hugegraph-python-client/src/pyhugegraph/structure/graph_instance.py
deleted file mode 100644
index 08b390e..0000000
--- a/hugegraph-python-client/src/pyhugegraph/structure/graph_instance.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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 GraphInstance:
-    def __init__(self, ip, port, graph_name, user_name, passwd, timeout):
-        self.__ip = ip
-        self.__port = port
-        # self.__graphspace = graphspace
-        self.__graph_name = graph_name
-        self.__user_name = user_name
-        self.__passwd = passwd
-        self.__timeout = timeout
-
-    @property
-    def ip(self):
-        return self.__ip
-
-    @property
-    def port(self):
-        return self.__port
-
-    @property
-    def graph_name(self):
-        return self.__graph_name
-
-    @property
-    def user_name(self):
-        return self.__user_name
-
-    @property
-    def passwd(self):
-        return self.__passwd
-
-    @property
-    def timeout(self):
-        return self.__timeout
-
-    def __repr__(self):
-        res = (
-            f"ip:{self.ip}, port:{self.port}, graph_name:{self.graph_name},"
-            f" user_name:{self.user_name}, passwd:{self.passwd}, 
timeout:{self.timeout}"
-        )
-        return res
diff --git 
a/hugegraph-python-client/src/pyhugegraph/structure/property_key_data.py 
b/hugegraph-python-client/src/pyhugegraph/structure/property_key_data.py
index 9b7b50c..6fb7c36 100644
--- a/hugegraph-python-client/src/pyhugegraph/structure/property_key_data.py
+++ b/hugegraph-python-client/src/pyhugegraph/structure/property_key_data.py
@@ -15,6 +15,23 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# from dataclasses import dataclass, field
+
+
+# @dataclass
+# class PropertyKeyData:
+#     id: int
+#     name: str
+#     cardinality: str
+#     data_type: str
+#     properties: list = field(default_factory=list)
+#     user_data: dict = field(default_factory=dict)
+
+#     @classmethod
+#     def from_dict(cls, data: dict):
+#         filtered_data = {k: v for k, v in data.items() if k in 
cls.__annotations__}
+#         return cls(**filtered_data)
+
 
 class PropertyKeyData:
     def __init__(self, dic):
@@ -45,7 +62,5 @@ class PropertyKeyData:
         return self.__user_data
 
     def __repr__(self):
-        res = (
-            f"name: {self.__name}, cardinality: {self.__cardinality}, 
data_type: {self.__data_type}"
-        )
+        res = f"name: {self.__name}, cardinality: {self.__cardinality}, 
data_type: {self.__data_type}"
         return res
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/huge_config.py 
b/hugegraph-python-client/src/pyhugegraph/utils/huge_config.py
new file mode 100644
index 0000000..f7e00d9
--- /dev/null
+++ b/hugegraph-python-client/src/pyhugegraph/utils/huge_config.py
@@ -0,0 +1,67 @@
+# 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.
+
+import re
+import requests
+import traceback
+
+from dataclasses import dataclass, field
+from typing import Optional, List
+
+
+@dataclass
+class HGraphConfig:
+    ip: str
+    port: str
+    username: str
+    password: str
+    graph_name: str
+    graphspace: Optional[str] = None
+    timeout: int = 10
+    gs_supported: bool = field(default=False, init=False)
+    version: List[int] = field(default_factory=list)
+
+    def __post_init__(self):
+
+        if self.graphspace is not None:
+            self.gs_supported = True
+
+        else:
+            try:
+                response = requests.get(
+                    f"http://{self.ip}:{self.port}/versions";, timeout=1
+                )
+                core = response.json()["versions"]["core"]
+                print(f"Retrieved API version information from the server: 
{core}.")
+
+                match = re.search("(\d+)\.(\d+)(?:\.(\d+))?(?:\.\d+)?", core)
+                major, minor, patch = map(int, match.groups())
+                self.version.extend([major, minor, patch])
+
+                if major >= 3:
+                    self.graphspace = "DEFAULT"
+                    self.gs_supported = True
+                    print(
+                        f"graph space is not set, default value 'DEFAULT' will 
be used."
+                    )
+
+            except Exception as e:
+                traceback.print_exception(e)
+                self.gs_supported = False
+                print(
+                    "Failed to retrieve API version information from the 
server, reverting to default v1."
+                )
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/huge_decorator.py 
b/hugegraph-python-client/src/pyhugegraph/utils/huge_decorator.py
index b7151f3..ecc94bf 100644
--- a/hugegraph-python-client/src/pyhugegraph/utils/huge_decorator.py
+++ b/hugegraph-python-client/src/pyhugegraph/utils/huge_decorator.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+
 from decorator import decorator
 
 from pyhugegraph.utils.exceptions import NotAuthorizedError
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/huge_requests.py 
b/hugegraph-python-client/src/pyhugegraph/utils/huge_requests.py
index 57ac233..83ecece 100644
--- a/hugegraph-python-client/src/pyhugegraph/utils/huge_requests.py
+++ b/hugegraph-python-client/src/pyhugegraph/utils/huge_requests.py
@@ -18,15 +18,101 @@
 import requests
 from requests.adapters import HTTPAdapter
 from urllib3.util.retry import Retry
+from urllib.parse import urljoin
 
+from typing import Any, Optional
+from pyhugegraph.utils.constants import Constants
+from pyhugegraph.utils.huge_config import HGraphConfig
 
-class HugeSession:
-    @staticmethod
-    def new_session():
-        session = requests.Session()
-        retry = Retry(connect=5, backoff_factor=1)
-        adapter = HTTPAdapter(max_retries=retry)
-        session.mount("http://";, adapter)
-        session.mount("https://";, adapter)
-        session.keep_alive = False
-        return session
+
+class HGraphSession:
+    def __init__(
+        self,
+        cfg: HGraphConfig,
+        retries: int = 5,
+        backoff_factor: int = 1,
+        status_forcelist=(500, 502, 504),
+        session: Optional[requests.Session] = None,
+    ):
+        """
+        Initialize the HGraphSession object.
+        :param retries: The maximum number of retries.
+        :param backoff_factor: The backoff factor, used to calculate the 
interval between retries.
+        :param status_forcelist: A list of status codes that trigger a retry.
+        :param session: An optional requests.Session instance, for testing or 
advanced use cases.
+        """
+        self._cfg = cfg
+        self._retries = retries
+        self._backoff_factor = backoff_factor
+        self._status_forcelist = status_forcelist
+        self._auth = (cfg.username, cfg.password)
+        self._headers = {"Content-Type": Constants.HEADER_CONTENT_TYPE}
+        self._timeout = cfg.timeout
+        self._session = session if session else requests.Session()
+        self.__configure_session()
+
+    def __configure_session(self):
+        """
+        Configure the retry strategy and connection adapter for the session.
+        """
+        retry_strategy = Retry(
+            total=self._retries,
+            read=self._retries,
+            connect=self._retries,
+            backoff_factor=self._backoff_factor,
+            status_forcelist=self._status_forcelist,
+        )
+        adapter = HTTPAdapter(max_retries=retry_strategy)
+        self._session.mount("http://";, adapter)
+        self._session.mount("https://";, adapter)
+        self._session.keep_alive = False
+        # logger.debug(
+        #     "Session configured with retries=%s and backoff_factor=%s",
+        #     self.retries,
+        #     self.backoff_factor,
+        # )
+
+    def resolve(self, path: str):
+        """
+        Constructs the full URL for the given pathinfo based on the session 
context and API version.
+
+        :param path: The pathinfo to be appended to the base URL.
+        :return: The fully resolved URL as a string.
+
+        When path is "/some/things":
+        - Since path starts with "/", it is considered an absolute path, and 
urljoin will replace the path part of the base URL.
+        - Assuming the base URL is 
"http://127.0.0.1:8000/graphspaces/default/graphs/test_graph/";
+        - The result will be "http://127.0.0.1:8000/some/things";
+
+        When path is "some/things":
+        - Since path is a relative path, urljoin will append it to the path 
part of the base URL.
+        - Assuming the base URL is 
"http://127.0.0.1:8000/graphspaces/default/graphs/test_graph/";
+        - The result will be 
"http://127.0.0.1:8000/graphspaces/default/graphs/test_graph/some/things";
+        """
+
+        url = f"http://{self._cfg.ip}:{self._cfg.port}/";
+        if self._cfg.gs_supported:
+            url = urljoin(
+                url,
+                
f"graphspaces/{self._cfg.graphspace}/graphs/{self._cfg.graph_name}/",
+            )
+        else:
+            url = urljoin(url, f"graphs/{self._cfg.graph_name}/")
+        return urljoin(url, path).strip("/")
+
+    def close(self):
+        self._session.close()
+
+    def request(self, path: str, method: str = "GET", **kwargs: Any):
+        try:
+            # print(method, self.resolve(path))
+            response = getattr(self._session, method.lower())(
+                self.resolve(path),
+                auth=self._auth,
+                headers=self._headers,
+                timeout=self._timeout,
+                **kwargs,
+            )
+            return response
+        except requests.RequestException as e:
+            raise
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/huge_router.py 
b/hugegraph-python-client/src/pyhugegraph/utils/huge_router.py
new file mode 100644
index 0000000..08f9311
--- /dev/null
+++ b/hugegraph-python-client/src/pyhugegraph/utils/huge_router.py
@@ -0,0 +1,130 @@
+# 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.
+
+import re
+import json
+import inspect
+import functools
+import threading
+
+from abc import ABC
+from typing import Any, Callable, Dict, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from pyhugegraph.api.common import HGraphContext
+
+
+class SingletonBase(type):
+    _instances = {}
+    _lock = threading.Lock()
+
+    def __call__(cls, *args, **kwargs):
+        """
+        Ensure that only one instance of the class is created.
+        If an instance already exists, return it; otherwise, create a new one.
+        This method is thread-safe.
+        """
+        with cls._lock:
+            if cls not in cls._instances:
+                cls._instances[cls] = super().__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
+class HGraphRouterManager(metaclass=SingletonBase):
+    def __init__(self):
+        self._routers = {}
+
+    def register(self, key, path):
+        self._routers.update({key: path})
+
+    def get_routers(self):
+        return self._routers
+
+    def __repr__(self) -> str:
+        return json.dumps(self._routers, indent=4)
+
+
+def http(method: str, path: str) -> Callable:
+    """
+    A decorator to format the pathinfo and inject a request function into the 
decorated method.
+
+    Args:
+        method (str): The HTTP method to be used (e.g., 'GET', 'POST').
+        path (str): The pathinfo template to be formatted with function 
arguments.
+
+    Returns:
+        Callable: The decorator function.
+    """
+
+    def decorator(func: Callable) -> Callable:
+        """Decorator function that modifies the original function."""
+        HGraphRouterManager().register(func.__qualname__, (method, path))
+
+        @functools.wraps(func)
+        def wrapper(self: "HGraphContext", *args: Any, **kwargs: Any) -> Any:
+            """
+            Wrapper function to format the pathinfo and create a partial 
request function.
+
+            Args:
+                self (HGraphContext): The instance of the class.
+                *args (Any): Positional arguments to the decorated function.
+                **kwargs (Any): Keyword arguments to the decorated function.
+
+            Returns:
+                Any: The result of the decorated function.
+            """
+            # If the pathinfo contains placeholders, format it with the actual 
arguments
+            if re.search(r"{\w+}", path):
+                sig = inspect.signature(func)
+                bound_args = sig.bind(self, *args, **kwargs)
+                bound_args.apply_defaults()
+                all_kwargs = dict(bound_args.arguments)
+                # Remove 'self' from the arguments used to format the pathinfo
+                all_kwargs.pop("self")
+                formatted_path = path.format(**all_kwargs)
+            else:
+                formatted_path = path
+
+            # todo: If possible, reduce unnecessary multiple creations.
+            cache_key = (func.__qualname__, method, formatted_path)
+            # Use functools.partial to create a partial function for making 
requests
+            make_request = functools.partial(self._sess.request, 
formatted_path, method)
+            # Store the partial function on the instance
+            setattr(self, f"_{func.__name__}_request", make_request)
+
+            return func(self, *args, **kwargs)
+
+        return wrapper
+
+    return decorator
+
+
+class HGraphRouter(ABC):
+
+    def _invoke_request(self, **kwargs: Any):
+        """
+        Make an HTTP request using the stored partial request function.
+
+        Args:
+            **kwargs (Any): Keyword arguments to be passed to the request 
function.
+
+        Returns:
+            Any: The response from the HTTP request.
+        """
+        frame = inspect.currentframe().f_back
+        fname = frame.f_code.co_name
+        return getattr(self, f"_{fname}_request")(**kwargs)
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/log.py 
b/hugegraph-python-client/src/pyhugegraph/utils/log.py
new file mode 100755
index 0000000..18dac65
--- /dev/null
+++ b/hugegraph-python-client/src/pyhugegraph/utils/log.py
@@ -0,0 +1,75 @@
+#  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.
+
+import logging
+from logging.handlers import TimedRotatingFileHandler
+import os
+
+# Set log format
+LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
+DATE_FORMAT = "%Y-%m-%d %H:%M:%S %p"
+
+
+# Function to configure logging path, default is "logs/output.log"
+# You could import it in "__init__.py" & use it in the whole package
+def init_log(log_file="logs/output.log"):
+    # Ensure the log directory exists
+    log_dir = os.path.dirname(log_file)
+    os.makedirs(log_dir, exist_ok=True)
+
+    # Create a logger
+    log = logging.getLogger(__name__)
+    log.setLevel(logging.DEBUG)
+
+    # Create a handler for writing to log file
+    file_handler = TimedRotatingFileHandler(log_file, when='midnight', 
interval=1, backupCount=3, encoding='utf-8')
+    file_handler.setLevel(logging.DEBUG)
+    file_handler.setFormatter(logging.Formatter(LOG_FORMAT, 
datefmt=DATE_FORMAT))
+    log.addHandler(file_handler)
+
+    # ANSI escape sequences for colors
+    class CustomConsoleHandler(logging.StreamHandler):
+        COLORS = {
+            "DEBUG": "\033[0;37m",  # White
+            "INFO": "\033[0;32m",  # Green
+            "WARNING": "\033[0;33m",  # Yellow
+            "ERROR": "\033[0;31m",  # Red
+            "CRITICAL": "\033[0;41m"  # Red background
+        }
+
+        def emit(self, record):
+            try:
+                msg = self.format(record)
+                level = record.levelname
+                color_prefix = self.COLORS.get(level, "\033[0;37m")  # Default 
to white
+                color_suffix = "\033[0m"  # Reset to default
+                stream = self.stream
+                stream.write(color_prefix + msg + color_suffix + 
self.terminator)
+                self.flush()
+            except Exception as e:
+                self.handleError(record)
+                log.error(f"Log Print Exception: {e}")
+
+    # Also output logs to the console
+    custom_handler = CustomConsoleHandler()
+    custom_handler.setLevel(logging.DEBUG)
+    custom_handler.setFormatter(logging.Formatter(LOG_FORMAT, 
datefmt=DATE_FORMAT))
+    log.addHandler(custom_handler)
+
+    return log
+
+
+# Default logger configuration
+log = init_log()
diff --git a/hugegraph-python-client/src/pyhugegraph/utils/util.py 
b/hugegraph-python-client/src/pyhugegraph/utils/util.py
index 6372587..5ca36b3 100644
--- a/hugegraph-python-client/src/pyhugegraph/utils/util.py
+++ b/hugegraph-python-client/src/pyhugegraph/utils/util.py
@@ -43,7 +43,9 @@ def check_if_authorized(response):
 
 
 def check_if_success(response, error=None):
-    if (not str(response.status_code).startswith("20")) and 
check_if_authorized(response):
+    if (not str(response.status_code).startswith("20")) and 
check_if_authorized(
+        response
+    ):
         if error is None:
             error = NotFoundError(response.content)
 
@@ -51,8 +53,10 @@ def check_if_success(response, error=None):
         req_body = req.body if req.body else "Empty body"
         response_body = response.text if response.text else "Empty body"
         # Log the detailed information
-        print(f"\033[93mError-Client:\n"
-              f"Request URL: {req.url}, Request Body: {req_body}\nResponse 
Body: "
-              f"{response_body}\033[0m")
+        print(
+            f"\033[93mError-Client:\n"
+            f"Request URL: {req.url}, Request Body: {req_body}\nResponse Body: 
"
+            f"{response_body}\033[0m"
+        )
         raise error
     return True
diff --git a/hugegraph-python-client/src/tests/api/test_auth.py 
b/hugegraph-python-client/src/tests/api/test_auth.py
index 10e6bad..d2d30cf 100644
--- a/hugegraph-python-client/src/tests/api/test_auth.py
+++ b/hugegraph-python-client/src/tests/api/test_auth.py
@@ -98,7 +98,9 @@ class TestAuthManager(unittest.TestCase):
         self.assertEqual(group["group_name"], "test_group")
 
         # Modify the group
-        group = self.auth.modify_group(group["id"], 
group_description="test_description")
+        group = self.auth.modify_group(
+            group["id"], group_description="test_description"
+        )
         self.assertEqual(group["group_description"], "test_description")
 
         # Delete the group
@@ -133,7 +135,9 @@ class TestAuthManager(unittest.TestCase):
             [{"type": "VERTEX", "label": "person", "properties": {"city": 
"Shanghai"}}],
         )
         # Verify the target was modified
-        self.assertEqual(target["target_resources"][0]["properties"]["city"], 
"Shanghai")
+        self.assertEqual(
+            target["target_resources"][0]["properties"]["city"], "Shanghai"
+        )
 
         # Delete the target
         self.auth.delete_target(target["id"])

Reply via email to