turingdb 0.2.5__py3-none-any.whl → 0.2.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of turingdb might be problematic. Click here for more details.

turingdb/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
1
  from .turingdb import TuringDB, TuringDBException
2
+ from .turingsh import main as turingsh
2
3
 
3
- __all__ = ["TuringDB", "TuringDBException"]
4
+ __all__ = ["TuringDB", "TuringDBException", "turingsh"]
turingdb/__init__.pyi CHANGED
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  from _typeshed import Incomplete
2
4
 
3
5
  class TuringDBException(Exception):
@@ -6,13 +8,24 @@ class TuringDBException(Exception):
6
8
  class TuringDB:
7
9
  DEFAULT_HEADERS: Incomplete
8
10
  host: Incomplete
9
- def __init__(self, instance_id: str = '', auth_token: str = '', host: str = 'https://engines.turingdb.ai/sdk') -> None: ...
11
+ def __init__(
12
+ self,
13
+ instance_id: str = "",
14
+ auth_token: str = "",
15
+ host: str = "https://engines.turingdb.ai/sdk",
16
+ ) -> None: ...
17
+ def try_reach(self, timeout: int = 5): ...
18
+ def warmup(self, timeout: int = 5): ...
10
19
  def list_available_graphs(self) -> list[str]: ...
11
20
  def list_loaded_graphs(self) -> list[str]: ...
21
+ def is_graph_loaded(self) -> bool: ...
12
22
  def load_graph(self, graph_name: str, raise_if_loaded: bool = True): ...
13
23
  def create_graph(self, graph_name: str): ...
14
24
  def query(self, query: str): ...
15
25
  def set_commit(self, commit: str): ...
16
26
  def set_change(self, change: str): ...
17
- def checkout(self, change: str = 'main', commit: str = 'HEAD'): ...
27
+ def checkout(self, change: str = "main", commit: str = "HEAD"): ...
18
28
  def set_graph(self, graph_name: str): ...
29
+ def get_graph(self) -> str: ...
30
+ def get_query_exec_time(self) -> Optional[float]: ...
31
+ def get_total_exec_time(self) -> Optional[float]: ...
turingdb/turingdb.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from typing import Optional
2
2
 
3
+ import time
4
+
3
5
 
4
6
  class TuringDBException(Exception):
5
7
  def __init__(self, message: str):
@@ -23,7 +25,16 @@ class TuringDB:
23
25
  import copy
24
26
 
25
27
  self.host = host
26
- self._client = httpx.Client(auth=None, verify=False, timeout=timeout)
28
+ self._client = httpx.Client(
29
+ auth=None,
30
+ verify=False,
31
+ timeout=timeout,
32
+ )
33
+ self._query_exec_time: Optional[float] = None
34
+ self._total_exec_time: Optional[float] = None
35
+ self._t0: float = 0
36
+ self._t1: float = 0
37
+ self._timeout = timeout
27
38
 
28
39
  self._params = {
29
40
  "graph": "default",
@@ -37,12 +48,25 @@ class TuringDB:
37
48
  if auth_token != "":
38
49
  self._headers["Authorization"] = f"Bearer {auth_token}"
39
50
 
51
+ def try_reach(self, timeout: int = 5):
52
+ self._client.timeout = timeout
53
+ self.list_available_graphs()
54
+ self._client.timeout = self._timeout
55
+
56
+ def warmup(self, timeout: int = 5):
57
+ self._client.timeout = timeout
58
+ self.query("LIST GRAPH")
59
+ self._client.timeout = self._timeout
60
+
40
61
  def list_available_graphs(self) -> list[str]:
41
62
  return self._send_request("list_avail_graphs")["data"]
42
63
 
43
64
  def list_loaded_graphs(self) -> list[str]:
44
65
  return self._send_request("list_loaded_graphs")["data"][0][0]
45
66
 
67
+ def is_graph_loaded(self) -> bool:
68
+ return self._send_request("is_graph_loaded", params={"graph": self.get_graph()})["data"]
69
+
46
70
  def load_graph(self, graph_name: str, raise_if_loaded: bool = True):
47
71
  try:
48
72
  return self._send_request("load_graph", params={"graph": graph_name})
@@ -85,6 +109,9 @@ class TuringDB:
85
109
  def set_graph(self, graph_name: str):
86
110
  self._params["graph"] = graph_name
87
111
 
112
+ def get_graph(self) -> str:
113
+ return self._params["graph"]
114
+
88
115
  def _send_request(
89
116
  self,
90
117
  path: str,
@@ -93,6 +120,10 @@ class TuringDB:
93
120
  ):
94
121
  import orjson
95
122
 
123
+ self._query_exec_time = None
124
+ self._total_exec_time = None
125
+ self._t0 = time.time()
126
+
96
127
  if data is None:
97
128
  data = ""
98
129
 
@@ -118,37 +149,35 @@ class TuringDB:
118
149
  err = f"{err}: {details}"
119
150
  raise TuringDBException(err)
120
151
 
152
+ self._t1 = time.time()
153
+ self._total_exec_time = (self._t1 - self._t0) * 1000
154
+
121
155
  return json
122
156
 
123
157
  def _parse_chunks(self, json: dict):
124
158
  import pandas as pd
125
159
  import numpy as np
126
160
 
127
- data_header = json["header"]
128
- column_names: list[str] = data_header["column_names"]
129
- column_types: list[str] = data_header["column_types"]
130
- dtypes: list[np.dtype] = []
131
161
  columns: list[list] = []
132
162
 
163
+ self._query_exec_time = json["time"]
164
+
133
165
  for chunk in json["data"]:
134
166
  for i, col in enumerate(chunk):
135
167
  if i >= len(columns):
136
- match column_types[i]:
137
- case "String":
138
- dtypes.append(np.dtype(np.dtypes.StringDType))
139
- case "Int64":
140
- dtypes.append(np.dtype(np.dtypes.Int64DType))
141
- case "UInt64":
142
- dtypes.append(np.dtype(np.dtypes.UInt64DType))
143
- case "Double":
144
- dtypes.append(np.dtype(np.dtypes.Float64DType))
145
- case "Boolean":
146
- dtypes.append(np.dtype(np.dtypes.BoolDType))
147
-
148
168
  columns.append([])
149
169
 
150
170
  columns[i] = columns[i] + col
151
171
 
152
- arrays = [np.array(columns[i], dtype=dtypes[i]) for i in range(len(columns))]
172
+ arrays = [np.array(columns[i]) for i in range(len(columns))]
173
+
174
+ self._t1 = time.time()
175
+ self._total_exec_time = (self._t1 - self._t0) * 1000
176
+
177
+ return pd.DataFrame(dict(zip(range(len(arrays)), arrays)), index=None)
178
+
179
+ def get_query_exec_time(self) -> Optional[float]:
180
+ return self._query_exec_time
153
181
 
154
- return pd.DataFrame(dict(zip(column_names, arrays)), index=None)
182
+ def get_total_exec_time(self) -> Optional[float]:
183
+ return self._total_exec_time
@@ -0,0 +1,3 @@
1
+ from .turingsh import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,42 @@
1
+ import re
2
+
3
+ from prompt_toolkit.formatted_text import HTML
4
+ from prompt_toolkit.shortcuts import print_formatted_text
5
+
6
+
7
+ def get_visible_width(text):
8
+ """Remove HTML-like tags and get actual visible character count"""
9
+ return len(re.sub(r"<[^>]*>", "", text)) - 2
10
+
11
+
12
+ def center_with_html(text, total_width):
13
+ """Center text accounting for HTML markup"""
14
+ visible_width = get_visible_width(text)
15
+ padding = (total_width - visible_width) // 2
16
+ return " " * padding + text + " " * (total_width - visible_width - padding)
17
+
18
+
19
+ def greet():
20
+
21
+ topbar = (
22
+ "<skyblue>╔═════════════════════════════════════════════════════╗</skyblue>\n"
23
+ )
24
+ botbar = (
25
+ "<skyblue>╚═════════════════════════════════════════════════════╝</skyblue>"
26
+ )
27
+ delimiter = "<skyblue>║</skyblue>"
28
+ title = "<b>TuringDB Interactive Shell</b>"
29
+ help_msg = "Type <i>help</i> for help"
30
+ quit_msg = "Type <i>quit</i> or press <i>Ctrl+D</i> to quit"
31
+
32
+ box_content_width = 51
33
+
34
+ print_formatted_text(
35
+ HTML(
36
+ f"{topbar}"
37
+ f"{delimiter}{center_with_html(title, box_content_width)}{delimiter}\n"
38
+ f"{delimiter}{center_with_html(help_msg, box_content_width)}{delimiter}\n"
39
+ f"{delimiter}{center_with_html(quit_msg, box_content_width)}{delimiter}\n"
40
+ f"{botbar}"
41
+ )
42
+ )
@@ -0,0 +1,298 @@
1
+ """
2
+ TuringDB Shell Client
3
+ """
4
+
5
+ import click
6
+ from prompt_toolkit import prompt
7
+ from prompt_toolkit.completion import WordCompleter
8
+ from prompt_toolkit.history import InMemoryHistory
9
+ from prompt_toolkit.shortcuts import print_formatted_text
10
+ from prompt_toolkit.formatted_text import HTML
11
+
12
+ from . import greeter
13
+ from turingdb import TuringDB, TuringDBException
14
+
15
+
16
+ class ShellException(Exception):
17
+ pass
18
+
19
+
20
+ def create_completer():
21
+ """Create command completer"""
22
+ commands = [
23
+ # Shell keywords
24
+ "cd",
25
+ "LIST_AVAIL_GRAPHS",
26
+ "LIST_LOADED_GRAPHS",
27
+ "help",
28
+ "quit",
29
+ "status",
30
+ # Query keywords
31
+ "DESCENDING",
32
+ "CONSTRAINT",
33
+ "MANDATORY",
34
+ "ASCENDING",
35
+ "OPTIONAL",
36
+ "CONTAINS",
37
+ "DISTINCT",
38
+ "EXTRACT",
39
+ "REQUIRE",
40
+ "COLLECT",
41
+ "STARTS",
42
+ "UNIQUE",
43
+ "FILTER",
44
+ "SINGLE",
45
+ "SCALAR",
46
+ "UNWIND",
47
+ "REMOVE",
48
+ "RETURN",
49
+ "CREATE",
50
+ "DELETE",
51
+ "DETACH",
52
+ "EXISTS",
53
+ "IS NOT",
54
+ "LIMIT",
55
+ "YIELD",
56
+ "MATCH",
57
+ "MERGE",
58
+ "ORDER",
59
+ "WHERE",
60
+ "UNION",
61
+ "FALSE",
62
+ "COUNT",
63
+ "DESC",
64
+ "CALL",
65
+ "NULL",
66
+ "TRUE",
67
+ "WHEN",
68
+ "NONE",
69
+ "THEN",
70
+ "ELSE",
71
+ "CASE",
72
+ "ENDS",
73
+ "DROP",
74
+ "SKIP",
75
+ "WITH",
76
+ "ANY",
77
+ "SET",
78
+ "ALL",
79
+ "ASC",
80
+ "NOT",
81
+ "END",
82
+ "XOR",
83
+ "FOR",
84
+ "ADD",
85
+ "AND",
86
+ "OR",
87
+ "IN",
88
+ "IS",
89
+ "BY",
90
+ "DO",
91
+ "OF",
92
+ "ON",
93
+ "IF",
94
+ "AS",
95
+ ]
96
+ return WordCompleter(commands, ignore_case=True)
97
+
98
+
99
+ @click.group(invoke_without_command=True)
100
+ @click.option("--host", "-l", default="https://engines.turingdb.ai/sdk")
101
+ @click.option("--auth-token", "-p", default="")
102
+ @click.option("--instance-id", "-i", default="")
103
+ @click.pass_context
104
+ def main(ctx, host, auth_token, instance_id):
105
+ """TuringDB Shell - Interactive database client"""
106
+ client = TuringDB(
107
+ host=host,
108
+ auth_token=auth_token,
109
+ instance_id=instance_id,
110
+ )
111
+
112
+ start_shell(client)
113
+
114
+
115
+ def start_shell(client: TuringDB):
116
+ """Start the interactive shell"""
117
+ history = InMemoryHistory()
118
+ completer = create_completer()
119
+
120
+ # Welcome message
121
+ greeter.greet()
122
+
123
+ try:
124
+ client.try_reach()
125
+ client.warmup()
126
+ except Exception as e:
127
+ print_formatted_text(HTML(f"<red>✘ Could not connect to TuringDB.\n{e}</red>"))
128
+ print()
129
+ return
130
+
131
+ print_formatted_text(HTML("<green>✓ Connected to TuringDB</green>"))
132
+ print()
133
+
134
+ shell_commands = {
135
+ "cd": change_graph,
136
+ "list_avail_graphs": list_available_graphs,
137
+ "list_loaded_graphs": list_loaded_graphs,
138
+ }
139
+
140
+ while True:
141
+ graph = client.get_graph()
142
+ try:
143
+ # Get user input with styling
144
+ user_input = prompt(
145
+ HTML(f"<b><cyan>turingdb:{graph}</cyan></b><gray>></gray> "),
146
+ completer=completer,
147
+ history=history,
148
+ multiline=False,
149
+ )
150
+
151
+ # Handle commands
152
+ cmd = user_input.strip()
153
+
154
+ if not cmd:
155
+ continue
156
+
157
+ cmd_lower = cmd.lower()
158
+ cmd_words = cmd_lower.split()
159
+
160
+ if len(cmd_words) == 0:
161
+ continue
162
+
163
+ if len(cmd_words) == 1:
164
+ if cmd_lower == "quit":
165
+ print_formatted_text(HTML("<yellow>Goodbye! 👋</yellow>"))
166
+ break
167
+ elif cmd_lower == "help":
168
+ show_help()
169
+ continue
170
+
171
+ if cmd_words[0] in shell_commands:
172
+ try:
173
+ shell_commands[cmd_words[0]](client, cmd_words)
174
+ except ShellException as e:
175
+ print_formatted_text(HTML(f"<red>✘ {e}</red>"))
176
+ continue
177
+
178
+ # Execute query
179
+ try:
180
+ result = client.query(cmd)
181
+ print_formatted_text(HTML(f"<white>{result}</white>"))
182
+ except TuringDBException as e:
183
+ print_formatted_text(HTML(f"<red>✘ {e}</red>"))
184
+ except Exception as e:
185
+ print_formatted_text(HTML(f"<red>✘ Fatal error: {e}</red>"))
186
+
187
+ exec_time = client.get_total_exec_time()
188
+ if exec_time is not None:
189
+ print_formatted_text(
190
+ HTML(
191
+ f"<white>Total execution time: <yellow>{exec_time:.3f}</yellow> milliseconds</white>"
192
+ )
193
+ )
194
+ query_time = client.get_query_exec_time()
195
+ if query_time is not None:
196
+ print_formatted_text(
197
+ HTML(
198
+ f"<white>Including query execution time: <yellow>{query_time:.3f}</yellow> milliseconds</white>"
199
+ )
200
+ )
201
+
202
+ print() # Add spacing
203
+
204
+ except KeyboardInterrupt:
205
+ print_formatted_text(
206
+ HTML('\n<yellow>Use "quit" or Ctrl+D to quit</yellow>')
207
+ )
208
+ continue
209
+ except EOFError:
210
+ print_formatted_text(HTML("\n<yellow>Goodbye! 👋</yellow>"))
211
+ break
212
+
213
+
214
+ def show_help():
215
+ """Display help information"""
216
+ help_text = """<b>Available Commands:</b>
217
+ <cyan>cd my_graph</cyan> - Change active graph
218
+ <cyan>LIST_AVAIL_GRAPHS</cyan> - List available graphs
219
+ <cyan>LIST_LOADED_GRAPHS</cyan> - List loaded graphs
220
+ <cyan>help</cyan> - Show this help
221
+ <cyan>EXIT</cyan> or <cyan>\\q</cyan> - Quit shell
222
+
223
+ <b>Example queries:</b>
224
+ <cyan>LOAD GRAPH my_graph</cyan> - Load graph
225
+ <cyan>MATCH (n) RETURN n.name</cyan> - Return all node names
226
+
227
+ <b>Tips:</b>
228
+ • Use <i>Tab</i> for command completion
229
+ • Use <i>↑/↓</i> arrows for command history"""
230
+
231
+ print_formatted_text(HTML(help_text))
232
+
233
+
234
+ def change_graph(client: TuringDB, args: list[str]):
235
+ """Change the current graph"""
236
+
237
+ if len(args) != 2:
238
+ raise ShellException(
239
+ "change_graph() missing 1 required positional argument: 'graph_name'"
240
+ )
241
+
242
+ graph_name = args[1]
243
+
244
+ try:
245
+ client.set_graph(graph_name)
246
+ if not client.is_graph_loaded():
247
+ print_formatted_text(
248
+ HTML(
249
+ f"<red>✘ Graph {graph_name} needs to be loaded with 'load graph \"{graph_name}\"'</red>"
250
+ )
251
+ )
252
+ client.set_graph("default")
253
+ else:
254
+ print_formatted_text(
255
+ HTML(f"<green>✓ Changed graph to {graph_name}</green>")
256
+ )
257
+ except TuringDBException as e:
258
+ print_formatted_text(HTML(f"<red>✘ {e}</red>"))
259
+ except Exception as e:
260
+ print_formatted_text(HTML(f"<red>✘ Fatal error: {e}</red>"))
261
+
262
+
263
+ def list_available_graphs(client: TuringDB, args: list[str]):
264
+ """List available graphs"""
265
+
266
+ if len(args) != 1:
267
+ raise ShellException(
268
+ "list_available_graphs() missing 0 required positional argument"
269
+ )
270
+
271
+ try:
272
+ graphs = client.list_available_graphs()
273
+ print_formatted_text(HTML(f"<white>{graphs}</white>"))
274
+ except TuringDBException as e:
275
+ print_formatted_text(HTML(f"<red>✘ {e}</red>"))
276
+ except Exception as e:
277
+ print_formatted_text(HTML(f"<red>✘ Fatal error: {e}</red>"))
278
+
279
+
280
+ def list_loaded_graphs(client: TuringDB, args: list[str]):
281
+ """List loaded graphs"""
282
+
283
+ if len(args) != 1:
284
+ raise ShellException(
285
+ "list_loaded_graphs() missing 0 required positional argument"
286
+ )
287
+
288
+ try:
289
+ graphs = client.list_loaded_graphs()
290
+ print_formatted_text(HTML(f"<white>{graphs}</white>"))
291
+ except TuringDBException as e:
292
+ print_formatted_text(HTML(f"<red>✘ {e}</red>"))
293
+ except Exception as e:
294
+ print_formatted_text(HTML(f"<red>✘ Fatal error: {e}</red>"))
295
+
296
+
297
+ if __name__ == "__main__":
298
+ main()
@@ -1,15 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turingdb
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: TuringDB python sdk
5
5
  Project-URL: Homepage, https://github.com/turing-db/turingdb-sdk-python
6
6
  Project-URL: Repository, https://github.com/turing-db/turingdb-sdk-python
7
7
  License-File: LICENSE.txt
8
8
  Requires-Python: >=3.10
9
+ Requires-Dist: click>=8.2.1
9
10
  Requires-Dist: httpx>=0.28.1
10
11
  Requires-Dist: orjson>=3.11.1
11
12
  Requires-Dist: pandas-stubs>=2.3.0.250703
12
13
  Requires-Dist: pandas>=2.3.1
14
+ Requires-Dist: prompt-toolkit>=3.0.52
13
15
  Description-Content-Type: text/markdown
14
16
 
15
17
  # turingdb-sdk-python
@@ -0,0 +1,12 @@
1
+ turingdb/__init__.py,sha256=ZtSKfXpIh23hJSxzysSB-l3u8bhG-obVWNSvJ906_dQ,146
2
+ turingdb/__init__.pyi,sha256=9KNytbSxto6geGA_RMdl66f_6LHE7QkO2-yNlTI8El4,1144
3
+ turingdb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ turingdb/turingdb.py,sha256=6lZ-mha0ZcC6Rn7lUF7V5X1g-jkK2KHKjqC9mAt1m5Y,5296
5
+ turingdb/turingsh/__init__.py,sha256=4wdl3RdVQbx-rKhI_7hDawkMtcJzRb90qG84JynT8Qo,47
6
+ turingdb/turingsh/greeter.py,sha256=lxbfT6eS89tZmDui92ZXTFDMzWzUST_ih8a-Zrf8sxY,1570
7
+ turingdb/turingsh/turingsh.py,sha256=vi2rQrjRZaafqF756AR5itoCiOFxTQmGkMWhm2lgmRE,8160
8
+ turingdb-0.2.6.dist-info/METADATA,sha256=md4mpFMQSX_xKVgS6wlUvdR1akAuriMsC_OamLxfqN0,585
9
+ turingdb-0.2.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ turingdb-0.2.6.dist-info/entry_points.txt,sha256=qbMJy64bcRtR1UOSnMV9Io5aWLZIFI8H7N4sv1mqUwA,47
11
+ turingdb-0.2.6.dist-info/licenses/LICENSE.txt,sha256=PgwD9hjeoyWO93yZQnxCyt0s-PlhXWLgqdCPpB91GhY,1078
12
+ turingdb-0.2.6.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ turingsh = turingdb:turingsh
@@ -1,8 +0,0 @@
1
- turingdb/__init__.py,sha256=zOhbnDfi_6Zf2aLn7mZho-xHIydeLd8-1TIT2_046Kg,95
2
- turingdb/__init__.pyi,sha256=O6ZMBGuBgXInR2anKZI3ycMZECIuhlsTUanZiHjZEqs,790
3
- turingdb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- turingdb/turingdb.py,sha256=xn4v-SLwjS-8v9YyriUhF7GgNBjOH1Du6cJZ42PYvzM,4808
5
- turingdb-0.2.5.dist-info/METADATA,sha256=YIF4mQepRtNVYst_knRfveBrTP3KoWEc7ZntECxYC7s,519
6
- turingdb-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
7
- turingdb-0.2.5.dist-info/licenses/LICENSE.txt,sha256=PgwD9hjeoyWO93yZQnxCyt0s-PlhXWLgqdCPpB91GhY,1078
8
- turingdb-0.2.5.dist-info/RECORD,,