singlestoredb 1.16.1__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.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env python
2
+ """SingleStoreDB Base Manager."""
3
+ import os
4
+ import sys
5
+ import time
6
+ from typing import Any
7
+ from typing import Dict
8
+ from typing import List
9
+ from typing import Optional
10
+ from typing import Union
11
+ from urllib.parse import urljoin
12
+
13
+ import jwt
14
+ import requests
15
+
16
+ from .. import config
17
+ from ..exceptions import ManagementError
18
+ from ..exceptions import OperationalError
19
+ from .utils import get_token
20
+
21
+
22
+ def set_organization(kwargs: Dict[str, Any]) -> None:
23
+ """Set the organization ID in the dictionary."""
24
+ if kwargs.get('params', {}).get('organizationID', None):
25
+ return
26
+
27
+ org = os.environ.get('SINGLESTOREDB_ORGANIZATION')
28
+ if org:
29
+ if 'params' not in kwargs:
30
+ kwargs['params'] = {}
31
+ kwargs['params']['organizationID'] = org
32
+
33
+
34
+ def is_jwt(token: str) -> bool:
35
+ """Is the given token a JWT?"""
36
+ try:
37
+ jwt.decode(token, options={'verify_signature': False})
38
+ return True
39
+ except jwt.DecodeError:
40
+ return False
41
+
42
+
43
+ class Manager(object):
44
+ """SingleStoreDB manager base class."""
45
+
46
+ #: Management API version if none is specified.
47
+ default_version = config.get_option('management.version') or 'v1'
48
+
49
+ #: Base URL if none is specified.
50
+ default_base_url = config.get_option('management.base_url') \
51
+ or 'https://api.singlestore.com'
52
+
53
+ #: Object type
54
+ obj_type = ''
55
+
56
+ def __init__(
57
+ self, access_token: Optional[str] = None, version: Optional[str] = None,
58
+ base_url: Optional[str] = None, *, organization_id: Optional[str] = None,
59
+ ):
60
+ from .. import __version__ as client_version
61
+ new_access_token = (
62
+ access_token or get_token()
63
+ )
64
+ if not new_access_token:
65
+ raise ManagementError(msg='No management token was configured.')
66
+
67
+ self._is_jwt = not access_token and new_access_token and is_jwt(new_access_token)
68
+ self._sess = requests.Session()
69
+ self._sess.headers.update({
70
+ 'Authorization': f'Bearer {new_access_token}',
71
+ 'Content-Type': 'application/json',
72
+ 'Accept': 'application/json',
73
+ 'User-Agent': f'SingleStoreDB-Python/{client_version}',
74
+ })
75
+
76
+ self._base_url = urljoin(
77
+ base_url
78
+ or config.get_option('management.base_url')
79
+ or type(self).default_base_url,
80
+ version or type(self).default_version,
81
+ ) + '/'
82
+
83
+ self._params: Dict[str, str] = {}
84
+ if organization_id:
85
+ self._params['organizationID'] = organization_id
86
+
87
+ def _check(
88
+ self, res: requests.Response, url: str, params: Dict[str, Any],
89
+ ) -> requests.Response:
90
+ """
91
+ Check the HTTP response status code and raise an exception as needed.
92
+
93
+ Parameters
94
+ ----------
95
+ res : requests.Response
96
+ HTTP response to check
97
+
98
+ Returns
99
+ -------
100
+ requests.Response
101
+
102
+ """
103
+ if config.get_option('debug.queries'):
104
+ print(os.path.join(self._base_url, url), params, file=sys.stderr)
105
+ if res.status_code >= 400:
106
+ txt = res.text.strip()
107
+ msg = f'{txt}: /{url}'
108
+ if params:
109
+ new_params = params.copy()
110
+ if 'json' in new_params:
111
+ for k, v in new_params['json'].items():
112
+ if 'password' in k.lower() and v:
113
+ new_params['json'][k] = '*' * len(v)
114
+ msg += ': {}'.format(str(new_params))
115
+ raise ManagementError(errno=res.status_code, msg=msg, response=txt)
116
+ return res
117
+
118
+ def _doit(
119
+ self,
120
+ method: str,
121
+ path: str,
122
+ *args: Any,
123
+ **kwargs: Any,
124
+ ) -> requests.Response:
125
+ """Perform HTTP request."""
126
+ # Refresh the JWT as needed
127
+ if self._is_jwt:
128
+ self._sess.headers.update({'Authorization': f'Bearer {get_token()}'})
129
+ return getattr(self._sess, method.lower())(
130
+ urljoin(self._base_url, path), *args, **kwargs,
131
+ )
132
+
133
+ def _get(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
134
+ """
135
+ Invoke a GET request.
136
+
137
+ Parameters
138
+ ----------
139
+ path : str
140
+ Path of the resource
141
+ *args : positional arguments, optional
142
+ Arguments to add to the GET request
143
+ **kwargs : keyword arguments, optional
144
+ Keyword arguments to add to the GET request
145
+
146
+ Returns
147
+ -------
148
+ requests.Response
149
+
150
+ """
151
+ if self._params:
152
+ params = dict(self._params)
153
+ params.update(kwargs.get('params', {}))
154
+ kwargs['params'] = params
155
+ set_organization(kwargs)
156
+ return self._check(self._doit('get', path, *args, **kwargs), path, kwargs)
157
+
158
+ def _post(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
159
+ """
160
+ Invoke a POST request.
161
+
162
+ Parameters
163
+ ----------
164
+ path : str
165
+ Path of the resource
166
+ *args : positional arguments, optional
167
+ Arguments to add to the POST request
168
+ **kwargs : keyword arguments, optional
169
+ Keyword arguments to add to the POST request
170
+
171
+ Returns
172
+ -------
173
+ requests.Response
174
+
175
+ """
176
+ if self._params:
177
+ params = dict(self._params)
178
+ params.update(kwargs.get('params', {}))
179
+ kwargs['params'] = params
180
+ set_organization(kwargs)
181
+ return self._check(self._doit('post', path, *args, **kwargs), path, kwargs)
182
+
183
+ def _put(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
184
+ """
185
+ Invoke a PUT request.
186
+
187
+ Parameters
188
+ ----------
189
+ path : str
190
+ Path of the resource
191
+ *args : positional arguments, optional
192
+ Arguments to add to the POST request
193
+ **kwargs : keyword arguments, optional
194
+ Keyword arguments to add to the POST request
195
+
196
+ Returns
197
+ -------
198
+ requests.Response
199
+
200
+ """
201
+ if self._params:
202
+ params = dict(self._params)
203
+ params.update(kwargs.get('params', {}))
204
+ kwargs['params'] = params
205
+ set_organization(kwargs)
206
+ return self._check(self._doit('put', path, *args, **kwargs), path, kwargs)
207
+
208
+ def _delete(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
209
+ """
210
+ Invoke a DELETE request.
211
+
212
+ Parameters
213
+ ----------
214
+ path : str
215
+ Path of the resource
216
+ *args : positional arguments, optional
217
+ Arguments to add to the DELETE request
218
+ **kwargs : keyword arguments, optional
219
+ Keyword arguments to add to the DELETE request
220
+
221
+ Returns
222
+ -------
223
+ requests.Response
224
+
225
+ """
226
+ if self._params:
227
+ params = dict(self._params)
228
+ params.update(kwargs.get('params', {}))
229
+ kwargs['params'] = params
230
+ set_organization(kwargs)
231
+ return self._check(self._doit('delete', path, *args, **kwargs), path, kwargs)
232
+
233
+ def _patch(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
234
+ """
235
+ Invoke a PATCH request.
236
+
237
+ Parameters
238
+ ----------
239
+ path : str
240
+ Path of the resource
241
+ *args : positional arguments, optional
242
+ Arguments to add to the PATCH request
243
+ **kwargs : keyword arguments, optional
244
+ Keyword arguments to add to the PATCH request
245
+
246
+ Returns
247
+ -------
248
+ requests.Response
249
+
250
+ """
251
+ if self._params:
252
+ params = dict(self._params)
253
+ params.update(kwargs.get('params', {}))
254
+ kwargs['params'] = params
255
+ set_organization(kwargs)
256
+ return self._check(self._doit('patch', path, *args, **kwargs), path, kwargs)
257
+
258
+ def _wait_on_state(
259
+ self,
260
+ out: Any,
261
+ state: Union[str, List[str]],
262
+ interval: int = 20,
263
+ timeout: int = 600,
264
+ ) -> Any:
265
+ """
266
+ Wait on server state before continuing.
267
+
268
+ Parameters
269
+ ----------
270
+ out : Any
271
+ Current object
272
+ state : str or List[str]
273
+ State(s) to wait for
274
+ interval : int, optional
275
+ Interval between each server poll
276
+ timeout : int, optional
277
+ Maximum time to wait before raising an exception
278
+
279
+ Raises
280
+ ------
281
+ ManagementError
282
+ If timeout is reached
283
+
284
+ Returns
285
+ -------
286
+ Same object type as `out`
287
+
288
+ """
289
+ states = [
290
+ x.lower().strip()
291
+ for x in (isinstance(state, str) and [state] or state)
292
+ ]
293
+
294
+ if getattr(out, 'state', None) is None:
295
+ raise ManagementError(
296
+ msg='{} object does not have a `state` attribute'.format(
297
+ type(out).__name__,
298
+ ),
299
+ )
300
+
301
+ while True:
302
+ if getattr(out, 'state').lower() in states:
303
+ break
304
+ if timeout <= 0:
305
+ raise ManagementError(
306
+ msg=f'Exceeded waiting time for {self.obj_type} to become '
307
+ '{}.'.format(', '.join(states)),
308
+ )
309
+ time.sleep(interval)
310
+ timeout -= interval
311
+ out = getattr(self, f'get_{self.obj_type}')(out.id)
312
+
313
+ return out
314
+
315
+ def _wait_on_endpoint(
316
+ self,
317
+ out: Any,
318
+ interval: int = 10,
319
+ timeout: int = 300,
320
+ ) -> Any:
321
+ """
322
+ Wait for the endpoint to be ready by attempting to connect.
323
+
324
+ Parameters
325
+ ----------
326
+ out : Any
327
+ Workspace object with a connect method
328
+ interval : int, optional
329
+ Interval between each connection attempt (default: 10 seconds)
330
+ timeout : int, optional
331
+ Maximum time to wait before raising an exception (default: 300 seconds)
332
+
333
+ Raises
334
+ ------
335
+ ManagementError
336
+ If timeout is reached or endpoint is not available
337
+
338
+ Returns
339
+ -------
340
+ Same object type as `out`
341
+
342
+ """
343
+ # Only wait if workload type is set which means we are in the
344
+ # notebook environment. Outside of the environment, the endpoint
345
+ # may not be reachable directly.
346
+ if not os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE', ''):
347
+ return out
348
+
349
+ if not hasattr(out, 'connect') or not out.connect:
350
+ raise ManagementError(
351
+ msg=f'{type(out).__name__} object does not have a valid endpoint',
352
+ )
353
+
354
+ while True:
355
+ try:
356
+ # Try to establish a connection to the endpoint using context manager
357
+ with out.connect(connect_timeout=5):
358
+ pass
359
+ except Exception as exc:
360
+ # If we get an 'access denied' error, that means that the server is
361
+ # up and we just aren't authenticating.
362
+ if isinstance(exc, OperationalError) and exc.errno == 1045:
363
+ break
364
+ # If connection fails, check timeout and retry
365
+ if timeout <= 0:
366
+ raise ManagementError(
367
+ msg=f'Exceeded waiting time for {self.obj_type} endpoint '
368
+ 'to become ready',
369
+ )
370
+ time.sleep(interval)
371
+ timeout -= interval
372
+
373
+ return out
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env python
2
+ """SingleStoreDB Cloud Organization."""
3
+ import datetime
4
+ from typing import Dict
5
+ from typing import List
6
+ from typing import Optional
7
+ from typing import Union
8
+
9
+ from ..exceptions import ManagementError
10
+ from .inference_api import InferenceAPIManager
11
+ from .job import JobsManager
12
+ from .manager import Manager
13
+ from .utils import vars_to_str
14
+
15
+
16
+ def listify(x: Union[str, List[str]]) -> List[str]:
17
+ if isinstance(x, list):
18
+ return x
19
+ return [x]
20
+
21
+
22
+ def stringify(x: Union[str, List[str]]) -> str:
23
+ if isinstance(x, list):
24
+ return x[0]
25
+ return x
26
+
27
+
28
+ class Secret(object):
29
+ """
30
+ SingleStoreDB secrets definition.
31
+
32
+ This object is not directly instantiated. It is used in results
33
+ of API calls on the :class:`Organization`. See :meth:`Organization.get_secret`.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ id: str,
39
+ name: str,
40
+ created_by: str,
41
+ created_at: Union[str, datetime.datetime],
42
+ last_updated_by: str,
43
+ last_updated_at: Union[str, datetime.datetime],
44
+ value: Optional[str] = None,
45
+ deleted_by: Optional[str] = None,
46
+ deleted_at: Optional[Union[str, datetime.datetime]] = None,
47
+ ):
48
+ # UUID of the secret
49
+ self.id = id
50
+
51
+ # Name of the secret
52
+ self.name = name
53
+
54
+ # Value of the secret
55
+ self.value = value
56
+
57
+ # User who created the secret
58
+ self.created_by = created_by
59
+
60
+ # Time when the secret was created
61
+ self.created_at = created_at
62
+
63
+ # UUID of the user who last updated the secret
64
+ self.last_updated_by = last_updated_by
65
+
66
+ # Time when the secret was last updated
67
+ self.last_updated_at = last_updated_at
68
+
69
+ # UUID of the user who deleted the secret
70
+ self.deleted_by = deleted_by
71
+
72
+ # Time when the secret was deleted
73
+ self.deleted_at = deleted_at
74
+
75
+ @classmethod
76
+ def from_dict(cls, obj: Dict[str, str]) -> 'Secret':
77
+ """
78
+ Construct a Secret from a dictionary of values.
79
+
80
+ Parameters
81
+ ----------
82
+ obj : dict
83
+ Dictionary of values
84
+
85
+ Returns
86
+ -------
87
+ :class:`Secret`
88
+
89
+ """
90
+ out = cls(
91
+ id=obj['secretID'],
92
+ name=obj['name'],
93
+ created_by=obj['createdBy'],
94
+ created_at=obj['createdAt'],
95
+ last_updated_by=obj['lastUpdatedBy'],
96
+ last_updated_at=obj['lastUpdatedAt'],
97
+ value=obj.get('value'),
98
+ deleted_by=obj.get('deletedBy'),
99
+ deleted_at=obj.get('deletedAt'),
100
+ )
101
+
102
+ return out
103
+
104
+ def __str__(self) -> str:
105
+ """Return string representation."""
106
+ return vars_to_str(self)
107
+
108
+ def __repr__(self) -> str:
109
+ """Return string representation."""
110
+ return str(self)
111
+
112
+
113
+ class Organization(object):
114
+ """
115
+ Organization in SingleStoreDB Cloud portal.
116
+
117
+ This object is not directly instantiated. It is used in results
118
+ of ``WorkspaceManager`` API calls.
119
+
120
+ See Also
121
+ --------
122
+ :attr:`WorkspaceManager.organization`
123
+
124
+ """
125
+
126
+ id: str
127
+ name: str
128
+ firewall_ranges: List[str]
129
+
130
+ def __init__(self, id: str, name: str, firewall_ranges: List[str]):
131
+ """Use :attr:`WorkspaceManager.organization` instead."""
132
+ #: Unique ID of the organization
133
+ self.id = id
134
+
135
+ #: Name of the organization
136
+ self.name = name
137
+
138
+ #: Firewall ranges of the organization
139
+ self.firewall_ranges = list(firewall_ranges)
140
+
141
+ self._manager: Optional[Manager] = None
142
+
143
+ def __str__(self) -> str:
144
+ """Return string representation."""
145
+ return vars_to_str(self)
146
+
147
+ def __repr__(self) -> str:
148
+ """Return string representation."""
149
+ return str(self)
150
+
151
+ def get_secret(self, name: str) -> Secret:
152
+ if self._manager is None:
153
+ raise ManagementError(msg='Organization not initialized')
154
+
155
+ res = self._manager._get('secrets', params=dict(name=name))
156
+
157
+ secrets = [Secret.from_dict(item) for item in res.json()['secrets']]
158
+
159
+ if len(secrets) == 0:
160
+ raise ManagementError(msg=f'Secret {name} not found')
161
+
162
+ if len(secrets) > 1:
163
+ raise ManagementError(msg=f'Multiple secrets found for {name}')
164
+
165
+ return secrets[0]
166
+
167
+ @classmethod
168
+ def from_dict(
169
+ cls,
170
+ obj: Dict[str, Union[str, List[str]]],
171
+ manager: Manager,
172
+ ) -> 'Organization':
173
+ """
174
+ Convert dictionary to an ``Organization`` object.
175
+
176
+ Parameters
177
+ ----------
178
+ obj : dict
179
+ Key-value pairs to retrieve organization information from
180
+ manager : WorkspaceManager, optional
181
+ The WorkspaceManager the Organization belongs to
182
+
183
+ Returns
184
+ -------
185
+ :class:`Organization`
186
+
187
+ """
188
+ out = cls(
189
+ id=stringify(obj['orgID']),
190
+ name=stringify(obj.get('name', '<unknown>')),
191
+ firewall_ranges=listify(obj.get('firewallRanges', [])),
192
+ )
193
+ out._manager = manager
194
+ return out
195
+
196
+ @property
197
+ def jobs(self) -> JobsManager:
198
+ """
199
+ Retrieve a SingleStoreDB scheduled job manager.
200
+
201
+ Parameters
202
+ ----------
203
+ manager : WorkspaceManager, optional
204
+ The WorkspaceManager the JobsManager belongs to
205
+
206
+ Returns
207
+ -------
208
+ :class:`JobsManager`
209
+ """
210
+ return JobsManager(self._manager)
211
+
212
+ @property
213
+ def inference_apis(self) -> InferenceAPIManager:
214
+ """
215
+ Retrieve a SingleStoreDB inference api manager.
216
+
217
+ Parameters
218
+ ----------
219
+ manager : WorkspaceManager, optional
220
+ The WorkspaceManager the InferenceAPIManager belongs to
221
+
222
+ Returns
223
+ -------
224
+ :class:`InferenceAPIManager`
225
+ """
226
+ return InferenceAPIManager(self._manager)