singlestoredb 1.8.0__py3-none-any.whl → 1.9.0__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 singlestoredb might be problematic. Click here for more details.
- singlestoredb/__init__.py +2 -2
- singlestoredb/config.py +6 -0
- singlestoredb/connection.py +11 -0
- singlestoredb/fusion/handler.py +3 -0
- singlestoredb/fusion/handlers/export.py +6 -14
- singlestoredb/fusion/handlers/files.py +690 -0
- singlestoredb/fusion/handlers/stage.py +103 -91
- singlestoredb/fusion/handlers/utils.py +148 -0
- singlestoredb/management/__init__.py +1 -0
- singlestoredb/management/export.py +20 -184
- singlestoredb/management/files.py +1038 -0
- singlestoredb/management/workspace.py +162 -349
- singlestoredb/mysql/connection.py +9 -1
- singlestoredb/tests/test.ipynb +18 -0
- singlestoredb/tests/test2.ipynb +18 -0
- singlestoredb/tests/test_management.py +273 -1
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/METADATA +1 -1
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/RECORD +24 -18
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/WHEEL +1 -1
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/top_level.txt +1 -0
- sqlx/__init__.py +4 -0
- sqlx/magic.py +113 -0
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.9.0.dist-info}/entry_points.txt +0 -0
|
@@ -21,6 +21,12 @@ from .. import config
|
|
|
21
21
|
from .. import connection
|
|
22
22
|
from ..exceptions import ManagementError
|
|
23
23
|
from .billing_usage import BillingUsageItem
|
|
24
|
+
from .files import FileLocation
|
|
25
|
+
from .files import FilesObject
|
|
26
|
+
from .files import FilesObjectBytesReader
|
|
27
|
+
from .files import FilesObjectBytesWriter
|
|
28
|
+
from .files import FilesObjectTextReader
|
|
29
|
+
from .files import FilesObjectTextWriter
|
|
24
30
|
from .manager import Manager
|
|
25
31
|
from .organization import Organization
|
|
26
32
|
from .region import Region
|
|
@@ -84,334 +90,17 @@ def get_workspace(
|
|
|
84
90
|
raise RuntimeError('no workspace group specified')
|
|
85
91
|
|
|
86
92
|
|
|
87
|
-
class
|
|
88
|
-
"""
|
|
89
|
-
Stage file / folder object.
|
|
90
|
-
|
|
91
|
-
This object is not instantiated directly. It is used in the results
|
|
92
|
-
of various operations in ``WorkspaceGroup.stage`` methods.
|
|
93
|
-
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
def __init__(
|
|
97
|
-
self,
|
|
98
|
-
name: str,
|
|
99
|
-
path: str,
|
|
100
|
-
size: int,
|
|
101
|
-
type: str,
|
|
102
|
-
format: str,
|
|
103
|
-
mimetype: str,
|
|
104
|
-
created: Optional[datetime.datetime],
|
|
105
|
-
last_modified: Optional[datetime.datetime],
|
|
106
|
-
writable: bool,
|
|
107
|
-
content: Optional[List[str]] = None,
|
|
108
|
-
):
|
|
109
|
-
#: Name of file / folder
|
|
110
|
-
self.name = name
|
|
111
|
-
|
|
112
|
-
if type == 'directory':
|
|
113
|
-
path = re.sub(r'/*$', r'', str(path)) + '/'
|
|
114
|
-
|
|
115
|
-
#: Path of file / folder
|
|
116
|
-
self.path = path
|
|
117
|
-
|
|
118
|
-
#: Size of the object (in bytes)
|
|
119
|
-
self.size = size
|
|
120
|
-
|
|
121
|
-
#: Data type: file or directory
|
|
122
|
-
self.type = type
|
|
123
|
-
|
|
124
|
-
#: Data format
|
|
125
|
-
self.format = format
|
|
126
|
-
|
|
127
|
-
#: Mime type
|
|
128
|
-
self.mimetype = mimetype
|
|
129
|
-
|
|
130
|
-
#: Datetime the object was created
|
|
131
|
-
self.created_at = created
|
|
132
|
-
|
|
133
|
-
#: Datetime the object was modified last
|
|
134
|
-
self.last_modified_at = last_modified
|
|
135
|
-
|
|
136
|
-
#: Is the object writable?
|
|
137
|
-
self.writable = writable
|
|
138
|
-
|
|
139
|
-
#: Contents of a directory
|
|
140
|
-
self.content: List[str] = content or []
|
|
141
|
-
|
|
142
|
-
self._stage: Optional[Stage] = None
|
|
143
|
-
|
|
144
|
-
@classmethod
|
|
145
|
-
def from_dict(
|
|
146
|
-
cls,
|
|
147
|
-
obj: Dict[str, Any],
|
|
148
|
-
stage: Stage,
|
|
149
|
-
) -> StageObject:
|
|
150
|
-
"""
|
|
151
|
-
Construct a StageObject from a dictionary of values.
|
|
152
|
-
|
|
153
|
-
Parameters
|
|
154
|
-
----------
|
|
155
|
-
obj : dict
|
|
156
|
-
Dictionary of values
|
|
157
|
-
stage : Stage
|
|
158
|
-
Stage object to use as the parent
|
|
159
|
-
|
|
160
|
-
Returns
|
|
161
|
-
-------
|
|
162
|
-
:class:`StageObject`
|
|
163
|
-
|
|
164
|
-
"""
|
|
165
|
-
out = cls(
|
|
166
|
-
name=obj['name'],
|
|
167
|
-
path=obj['path'],
|
|
168
|
-
size=obj['size'],
|
|
169
|
-
type=obj['type'],
|
|
170
|
-
format=obj['format'],
|
|
171
|
-
mimetype=obj['mimetype'],
|
|
172
|
-
created=to_datetime(obj.get('created')),
|
|
173
|
-
last_modified=to_datetime(obj.get('last_modified')),
|
|
174
|
-
writable=bool(obj['writable']),
|
|
175
|
-
)
|
|
176
|
-
out._stage = stage
|
|
177
|
-
return out
|
|
178
|
-
|
|
179
|
-
def __str__(self) -> str:
|
|
180
|
-
"""Return string representation."""
|
|
181
|
-
return vars_to_str(self)
|
|
182
|
-
|
|
183
|
-
def __repr__(self) -> str:
|
|
184
|
-
"""Return string representation."""
|
|
185
|
-
return str(self)
|
|
186
|
-
|
|
187
|
-
def open(
|
|
188
|
-
self,
|
|
189
|
-
mode: str = 'r',
|
|
190
|
-
encoding: Optional[str] = None,
|
|
191
|
-
) -> Union[io.StringIO, io.BytesIO]:
|
|
192
|
-
"""
|
|
193
|
-
Open a Stage path for reading or writing.
|
|
194
|
-
|
|
195
|
-
Parameters
|
|
196
|
-
----------
|
|
197
|
-
mode : str, optional
|
|
198
|
-
The read / write mode. The following modes are supported:
|
|
199
|
-
* 'r' open for reading (default)
|
|
200
|
-
* 'w' open for writing, truncating the file first
|
|
201
|
-
* 'x' create a new file and open it for writing
|
|
202
|
-
The data type can be specified by adding one of the following:
|
|
203
|
-
* 'b' binary mode
|
|
204
|
-
* 't' text mode (default)
|
|
205
|
-
encoding : str, optional
|
|
206
|
-
The string encoding to use for text
|
|
207
|
-
|
|
208
|
-
Returns
|
|
209
|
-
-------
|
|
210
|
-
StageObjectBytesReader - 'rb' or 'b' mode
|
|
211
|
-
StageObjectBytesWriter - 'wb' or 'xb' mode
|
|
212
|
-
StageObjectTextReader - 'r' or 'rt' mode
|
|
213
|
-
StageObjectTextWriter - 'w', 'x', 'wt' or 'xt' mode
|
|
214
|
-
|
|
215
|
-
"""
|
|
216
|
-
if self._stage is None:
|
|
217
|
-
raise ManagementError(
|
|
218
|
-
msg='No Stage object is associated with this object.',
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
if self.is_dir():
|
|
222
|
-
raise IsADirectoryError(
|
|
223
|
-
f'directories can not be read or written: {self.path}',
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
return self._stage.open(self.path, mode=mode, encoding=encoding)
|
|
227
|
-
|
|
228
|
-
def download(
|
|
229
|
-
self,
|
|
230
|
-
local_path: Optional[PathLike] = None,
|
|
231
|
-
*,
|
|
232
|
-
overwrite: bool = False,
|
|
233
|
-
encoding: Optional[str] = None,
|
|
234
|
-
) -> Optional[Union[bytes, str]]:
|
|
235
|
-
"""
|
|
236
|
-
Download the content of a stage path.
|
|
237
|
-
|
|
238
|
-
Parameters
|
|
239
|
-
----------
|
|
240
|
-
local_path : Path or str
|
|
241
|
-
Path to local file target location
|
|
242
|
-
overwrite : bool, optional
|
|
243
|
-
Should an existing file be overwritten if it exists?
|
|
244
|
-
encoding : str, optional
|
|
245
|
-
Encoding used to convert the resulting data
|
|
246
|
-
|
|
247
|
-
Returns
|
|
248
|
-
-------
|
|
249
|
-
bytes or str or None
|
|
250
|
-
|
|
251
|
-
"""
|
|
252
|
-
if self._stage is None:
|
|
253
|
-
raise ManagementError(
|
|
254
|
-
msg='No Stage object is associated with this object.',
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
return self._stage.download_file(
|
|
258
|
-
self.path, local_path=local_path,
|
|
259
|
-
overwrite=overwrite, encoding=encoding,
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
download_file = download
|
|
263
|
-
|
|
264
|
-
def remove(self) -> None:
|
|
265
|
-
"""Delete the stage file."""
|
|
266
|
-
if self._stage is None:
|
|
267
|
-
raise ManagementError(
|
|
268
|
-
msg='No Stage object is associated with this object.',
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
if self.type == 'directory':
|
|
272
|
-
raise IsADirectoryError(
|
|
273
|
-
f'path is a directory; use rmdir or removedirs {self.path}',
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
self._stage.remove(self.path)
|
|
277
|
-
|
|
278
|
-
def rmdir(self) -> None:
|
|
279
|
-
"""Delete the empty stage directory."""
|
|
280
|
-
if self._stage is None:
|
|
281
|
-
raise ManagementError(
|
|
282
|
-
msg='No Stage object is associated with this object.',
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
if self.type != 'directory':
|
|
286
|
-
raise NotADirectoryError(
|
|
287
|
-
f'path is not a directory: {self.path}',
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
self._stage.rmdir(self.path)
|
|
291
|
-
|
|
292
|
-
def removedirs(self) -> None:
|
|
293
|
-
"""Delete the stage directory recursively."""
|
|
294
|
-
if self._stage is None:
|
|
295
|
-
raise ManagementError(
|
|
296
|
-
msg='No Stage object is associated with this object.',
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
if self.type != 'directory':
|
|
300
|
-
raise NotADirectoryError(
|
|
301
|
-
f'path is not a directory: {self.path}',
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
self._stage.removedirs(self.path)
|
|
305
|
-
|
|
306
|
-
def rename(self, new_path: PathLike, *, overwrite: bool = False) -> None:
|
|
307
|
-
"""
|
|
308
|
-
Move the stage file to a new location.
|
|
309
|
-
|
|
310
|
-
Parameters
|
|
311
|
-
----------
|
|
312
|
-
new_path : Path or str
|
|
313
|
-
The new location of the file
|
|
314
|
-
overwrite : bool, optional
|
|
315
|
-
Should path be overwritten if it already exists?
|
|
316
|
-
|
|
317
|
-
"""
|
|
318
|
-
if self._stage is None:
|
|
319
|
-
raise ManagementError(
|
|
320
|
-
msg='No Stage object is associated with this object.',
|
|
321
|
-
)
|
|
322
|
-
out = self._stage.rename(self.path, new_path, overwrite=overwrite)
|
|
323
|
-
self.name = out.name
|
|
324
|
-
self.path = out.path
|
|
325
|
-
return None
|
|
326
|
-
|
|
327
|
-
def exists(self) -> bool:
|
|
328
|
-
"""Does the file / folder exist?"""
|
|
329
|
-
if self._stage is None:
|
|
330
|
-
raise ManagementError(
|
|
331
|
-
msg='No Stage object is associated with this object.',
|
|
332
|
-
)
|
|
333
|
-
return self._stage.exists(self.path)
|
|
334
|
-
|
|
335
|
-
def is_dir(self) -> bool:
|
|
336
|
-
"""Is the stage object a directory?"""
|
|
337
|
-
return self.type == 'directory'
|
|
338
|
-
|
|
339
|
-
def is_file(self) -> bool:
|
|
340
|
-
"""Is the stage object a file?"""
|
|
341
|
-
return self.type != 'directory'
|
|
342
|
-
|
|
343
|
-
def abspath(self) -> str:
|
|
344
|
-
"""Return the full path of the object."""
|
|
345
|
-
return str(self.path)
|
|
346
|
-
|
|
347
|
-
def basename(self) -> str:
|
|
348
|
-
"""Return the basename of the object."""
|
|
349
|
-
return self.name
|
|
350
|
-
|
|
351
|
-
def dirname(self) -> str:
|
|
352
|
-
"""Return the directory name of the object."""
|
|
353
|
-
return re.sub(r'/*$', r'', os.path.dirname(re.sub(r'/*$', r'', self.path))) + '/'
|
|
354
|
-
|
|
355
|
-
def getmtime(self) -> float:
|
|
356
|
-
"""Return the last modified datetime as a UNIX timestamp."""
|
|
357
|
-
if self.last_modified_at is None:
|
|
358
|
-
return 0.0
|
|
359
|
-
return self.last_modified_at.timestamp()
|
|
360
|
-
|
|
361
|
-
def getctime(self) -> float:
|
|
362
|
-
"""Return the creation datetime as a UNIX timestamp."""
|
|
363
|
-
if self.created_at is None:
|
|
364
|
-
return 0.0
|
|
365
|
-
return self.created_at.timestamp()
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
class StageObjectTextWriter(io.StringIO):
|
|
369
|
-
"""StringIO wrapper for writing to Stage."""
|
|
370
|
-
|
|
371
|
-
def __init__(self, buffer: Optional[str], stage: Stage, stage_path: PathLike):
|
|
372
|
-
self._stage = stage
|
|
373
|
-
self._stage_path = stage_path
|
|
374
|
-
super().__init__(buffer)
|
|
375
|
-
|
|
376
|
-
def close(self) -> None:
|
|
377
|
-
"""Write the content to the stage path."""
|
|
378
|
-
self._stage._upload(self.getvalue(), self._stage_path)
|
|
379
|
-
super().close()
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
class StageObjectTextReader(io.StringIO):
|
|
383
|
-
"""StringIO wrapper for reading from Stage."""
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
class StageObjectBytesWriter(io.BytesIO):
|
|
387
|
-
"""BytesIO wrapper for writing to Stage."""
|
|
388
|
-
|
|
389
|
-
def __init__(self, buffer: bytes, stage: Stage, stage_path: PathLike):
|
|
390
|
-
self._stage = stage
|
|
391
|
-
self._stage_path = stage_path
|
|
392
|
-
super().__init__(buffer)
|
|
393
|
-
|
|
394
|
-
def close(self) -> None:
|
|
395
|
-
"""Write the content to the stage path."""
|
|
396
|
-
self._stage._upload(self.getvalue(), self._stage_path)
|
|
397
|
-
super().close()
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
class StageObjectBytesReader(io.BytesIO):
|
|
401
|
-
"""BytesIO wrapper for reading from Stage."""
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
class Stage(object):
|
|
93
|
+
class Stage(FileLocation):
|
|
405
94
|
"""
|
|
406
95
|
Stage manager.
|
|
407
96
|
|
|
408
97
|
This object is not instantiated directly.
|
|
409
|
-
It is returned by ``WorkspaceGroup.stage``.
|
|
98
|
+
It is returned by ``WorkspaceGroup.stage`` or ``StarterWorkspace.stage``.
|
|
410
99
|
|
|
411
100
|
"""
|
|
412
101
|
|
|
413
|
-
def __init__(self,
|
|
414
|
-
self.
|
|
102
|
+
def __init__(self, deployment_id: str, manager: WorkspaceManager):
|
|
103
|
+
self._deployment_id = deployment_id
|
|
415
104
|
self._manager = manager
|
|
416
105
|
|
|
417
106
|
def open(
|
|
@@ -440,10 +129,10 @@ class Stage(object):
|
|
|
440
129
|
|
|
441
130
|
Returns
|
|
442
131
|
-------
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
132
|
+
FilesObjectBytesReader - 'rb' or 'b' mode
|
|
133
|
+
FilesObjectBytesWriter - 'wb' or 'xb' mode
|
|
134
|
+
FilesObjectTextReader - 'r' or 'rt' mode
|
|
135
|
+
FilesObjectTextWriter - 'w', 'x', 'wt' or 'xt' mode
|
|
447
136
|
|
|
448
137
|
"""
|
|
449
138
|
if '+' in mode or 'a' in mode:
|
|
@@ -456,19 +145,19 @@ class Stage(object):
|
|
|
456
145
|
raise FileExistsError(f'stage path already exists: {stage_path}')
|
|
457
146
|
self.remove(stage_path)
|
|
458
147
|
if 'b' in mode:
|
|
459
|
-
return
|
|
460
|
-
return
|
|
148
|
+
return FilesObjectBytesWriter(b'', self, stage_path)
|
|
149
|
+
return FilesObjectTextWriter('', self, stage_path)
|
|
461
150
|
|
|
462
151
|
if 'r' in mode:
|
|
463
152
|
content = self.download_file(stage_path)
|
|
464
153
|
if isinstance(content, bytes):
|
|
465
154
|
if 'b' in mode:
|
|
466
|
-
return
|
|
155
|
+
return FilesObjectBytesReader(content)
|
|
467
156
|
encoding = 'utf-8' if encoding is None else encoding
|
|
468
|
-
return
|
|
157
|
+
return FilesObjectTextReader(content.decode(encoding))
|
|
469
158
|
|
|
470
159
|
if isinstance(content, str):
|
|
471
|
-
return
|
|
160
|
+
return FilesObjectTextReader(content)
|
|
472
161
|
|
|
473
162
|
raise ValueError(f'unrecognized file content type: {type(content)}')
|
|
474
163
|
|
|
@@ -480,7 +169,7 @@ class Stage(object):
|
|
|
480
169
|
stage_path: PathLike,
|
|
481
170
|
*,
|
|
482
171
|
overwrite: bool = False,
|
|
483
|
-
) ->
|
|
172
|
+
) -> FilesObject:
|
|
484
173
|
"""
|
|
485
174
|
Upload a local file.
|
|
486
175
|
|
|
@@ -518,7 +207,7 @@ class Stage(object):
|
|
|
518
207
|
recursive: bool = True,
|
|
519
208
|
include_root: bool = False,
|
|
520
209
|
ignore: Optional[Union[PathLike, List[PathLike]]] = None,
|
|
521
|
-
) ->
|
|
210
|
+
) -> FilesObject:
|
|
522
211
|
"""
|
|
523
212
|
Upload a folder recursively.
|
|
524
213
|
|
|
@@ -573,7 +262,7 @@ class Stage(object):
|
|
|
573
262
|
stage_path: PathLike,
|
|
574
263
|
*,
|
|
575
264
|
overwrite: bool = False,
|
|
576
|
-
) ->
|
|
265
|
+
) -> FilesObject:
|
|
577
266
|
"""
|
|
578
267
|
Upload content to a stage file.
|
|
579
268
|
|
|
@@ -593,14 +282,14 @@ class Stage(object):
|
|
|
593
282
|
self.remove(stage_path)
|
|
594
283
|
|
|
595
284
|
self._manager._put(
|
|
596
|
-
f'stage/{self.
|
|
285
|
+
f'stage/{self._deployment_id}/fs/{stage_path}',
|
|
597
286
|
files={'file': content},
|
|
598
287
|
headers={'Content-Type': None},
|
|
599
288
|
)
|
|
600
289
|
|
|
601
290
|
return self.info(stage_path)
|
|
602
291
|
|
|
603
|
-
def mkdir(self, stage_path: PathLike, overwrite: bool = False) ->
|
|
292
|
+
def mkdir(self, stage_path: PathLike, overwrite: bool = False) -> FilesObject:
|
|
604
293
|
"""
|
|
605
294
|
Make a directory in the stage.
|
|
606
295
|
|
|
@@ -613,7 +302,7 @@ class Stage(object):
|
|
|
613
302
|
|
|
614
303
|
Returns
|
|
615
304
|
-------
|
|
616
|
-
|
|
305
|
+
FilesObject
|
|
617
306
|
|
|
618
307
|
"""
|
|
619
308
|
stage_path = re.sub(r'/*$', r'', str(stage_path)) + '/'
|
|
@@ -625,7 +314,7 @@ class Stage(object):
|
|
|
625
314
|
self.remove(stage_path)
|
|
626
315
|
|
|
627
316
|
self._manager._put(
|
|
628
|
-
f'stage/{self.
|
|
317
|
+
f'stage/{self._deployment_id}/fs/{stage_path}?isFile=false',
|
|
629
318
|
)
|
|
630
319
|
|
|
631
320
|
return self.info(stage_path)
|
|
@@ -638,7 +327,7 @@ class Stage(object):
|
|
|
638
327
|
new_path: PathLike,
|
|
639
328
|
*,
|
|
640
329
|
overwrite: bool = False,
|
|
641
|
-
) ->
|
|
330
|
+
) -> FilesObject:
|
|
642
331
|
"""
|
|
643
332
|
Move the stage file to a new location.
|
|
644
333
|
|
|
@@ -668,13 +357,13 @@ class Stage(object):
|
|
|
668
357
|
self.remove(new_path)
|
|
669
358
|
|
|
670
359
|
self._manager._patch(
|
|
671
|
-
f'stage/{self.
|
|
360
|
+
f'stage/{self._deployment_id}/fs/{old_path}',
|
|
672
361
|
json=dict(newPath=new_path),
|
|
673
362
|
)
|
|
674
363
|
|
|
675
364
|
return self.info(new_path)
|
|
676
365
|
|
|
677
|
-
def info(self, stage_path: PathLike) ->
|
|
366
|
+
def info(self, stage_path: PathLike) -> FilesObject:
|
|
678
367
|
"""
|
|
679
368
|
Return information about a stage location.
|
|
680
369
|
|
|
@@ -685,15 +374,15 @@ class Stage(object):
|
|
|
685
374
|
|
|
686
375
|
Returns
|
|
687
376
|
-------
|
|
688
|
-
|
|
377
|
+
FilesObject
|
|
689
378
|
|
|
690
379
|
"""
|
|
691
380
|
res = self._manager._get(
|
|
692
|
-
re.sub(r'/+$', r'/', f'stage/{self.
|
|
381
|
+
re.sub(r'/+$', r'/', f'stage/{self._deployment_id}/fs/{stage_path}'),
|
|
693
382
|
params=dict(metadata=1),
|
|
694
383
|
).json()
|
|
695
384
|
|
|
696
|
-
return
|
|
385
|
+
return FilesObject.from_dict(res, self)
|
|
697
386
|
|
|
698
387
|
def exists(self, stage_path: PathLike) -> bool:
|
|
699
388
|
"""
|
|
@@ -772,7 +461,7 @@ class Stage(object):
|
|
|
772
461
|
|
|
773
462
|
"""
|
|
774
463
|
res = self._manager._get(
|
|
775
|
-
f'stage/{self.
|
|
464
|
+
f'stage/{self._deployment_id}/fs/{stage_path}',
|
|
776
465
|
).json()
|
|
777
466
|
if recursive:
|
|
778
467
|
out = []
|
|
@@ -848,7 +537,7 @@ class Stage(object):
|
|
|
848
537
|
raise IsADirectoryError(f'stage path is a directory: {stage_path}')
|
|
849
538
|
|
|
850
539
|
out = self._manager._get(
|
|
851
|
-
f'stage/{self.
|
|
540
|
+
f'stage/{self._deployment_id}/fs/{stage_path}',
|
|
852
541
|
).content
|
|
853
542
|
|
|
854
543
|
if local_path is not None:
|
|
@@ -912,7 +601,7 @@ class Stage(object):
|
|
|
912
601
|
f'use rmdir or removedirs: {stage_path}',
|
|
913
602
|
)
|
|
914
603
|
|
|
915
|
-
self._manager._delete(f'stage/{self.
|
|
604
|
+
self._manager._delete(f'stage/{self._deployment_id}/fs/{stage_path}')
|
|
916
605
|
|
|
917
606
|
def removedirs(self, stage_path: PathLike) -> None:
|
|
918
607
|
"""
|
|
@@ -925,7 +614,7 @@ class Stage(object):
|
|
|
925
614
|
|
|
926
615
|
"""
|
|
927
616
|
stage_path = re.sub(r'/*$', r'', str(stage_path)) + '/'
|
|
928
|
-
self._manager._delete(f'stage/{self.
|
|
617
|
+
self._manager._delete(f'stage/{self._deployment_id}/fs/{stage_path}')
|
|
929
618
|
|
|
930
619
|
def rmdir(self, stage_path: PathLike) -> None:
|
|
931
620
|
"""
|
|
@@ -942,7 +631,7 @@ class Stage(object):
|
|
|
942
631
|
if self.listdir(stage_path):
|
|
943
632
|
raise OSError(f'stage folder is not empty, use removedirs: {stage_path}')
|
|
944
633
|
|
|
945
|
-
self._manager._delete(f'stage/{self.
|
|
634
|
+
self._manager._delete(f'stage/{self._deployment_id}/fs/{stage_path}')
|
|
946
635
|
|
|
947
636
|
def __str__(self) -> str:
|
|
948
637
|
"""Return string representation."""
|
|
@@ -953,6 +642,9 @@ class Stage(object):
|
|
|
953
642
|
return str(self)
|
|
954
643
|
|
|
955
644
|
|
|
645
|
+
StageObject = FilesObject # alias for backward compatibility
|
|
646
|
+
|
|
647
|
+
|
|
956
648
|
class Workspace(object):
|
|
957
649
|
"""
|
|
958
650
|
SingleStoreDB workspace definition.
|
|
@@ -1411,7 +1103,7 @@ class WorkspaceGroup(object):
|
|
|
1411
1103
|
raise ManagementError(
|
|
1412
1104
|
msg='No workspace manager is associated with this object.',
|
|
1413
1105
|
)
|
|
1414
|
-
return Stage(self, self._manager)
|
|
1106
|
+
return Stage(self.id, self._manager)
|
|
1415
1107
|
|
|
1416
1108
|
stages = stage
|
|
1417
1109
|
|
|
@@ -1598,6 +1290,104 @@ class WorkspaceGroup(object):
|
|
|
1598
1290
|
)
|
|
1599
1291
|
|
|
1600
1292
|
|
|
1293
|
+
class StarterWorkspace(object):
|
|
1294
|
+
"""
|
|
1295
|
+
SingleStoreDB starter workspace definition.
|
|
1296
|
+
|
|
1297
|
+
This object is not instantiated directly. It is used in the results
|
|
1298
|
+
of API calls on the :class:`WorkspaceManager`. Existing starter workspaces are
|
|
1299
|
+
accessed by either :attr:`WorkspaceManager.starter_workspaces` or by calling
|
|
1300
|
+
:meth:`WorkspaceManager.get_starter_workspace`.
|
|
1301
|
+
|
|
1302
|
+
See Also
|
|
1303
|
+
--------
|
|
1304
|
+
:meth:`WorkspaceManager.get_starter_workspace`
|
|
1305
|
+
:attr:`WorkspaceManager.starter_workspaces`
|
|
1306
|
+
|
|
1307
|
+
"""
|
|
1308
|
+
|
|
1309
|
+
name: str
|
|
1310
|
+
id: str
|
|
1311
|
+
|
|
1312
|
+
def __init__(
|
|
1313
|
+
self,
|
|
1314
|
+
name: str,
|
|
1315
|
+
id: str,
|
|
1316
|
+
):
|
|
1317
|
+
#: Name of the starter workspace
|
|
1318
|
+
self.name = name
|
|
1319
|
+
|
|
1320
|
+
#: Unique ID of the starter workspace
|
|
1321
|
+
self.id = id
|
|
1322
|
+
|
|
1323
|
+
self._manager: Optional[WorkspaceManager] = None
|
|
1324
|
+
|
|
1325
|
+
def __str__(self) -> str:
|
|
1326
|
+
"""Return string representation."""
|
|
1327
|
+
return vars_to_str(self)
|
|
1328
|
+
|
|
1329
|
+
def __repr__(self) -> str:
|
|
1330
|
+
"""Return string representation."""
|
|
1331
|
+
return str(self)
|
|
1332
|
+
|
|
1333
|
+
@classmethod
|
|
1334
|
+
def from_dict(
|
|
1335
|
+
cls, obj: Dict[str, Any], manager: 'WorkspaceManager',
|
|
1336
|
+
) -> 'StarterWorkspace':
|
|
1337
|
+
"""
|
|
1338
|
+
Construct a StarterWorkspace from a dictionary of values.
|
|
1339
|
+
|
|
1340
|
+
Parameters
|
|
1341
|
+
----------
|
|
1342
|
+
obj : dict
|
|
1343
|
+
Dictionary of values
|
|
1344
|
+
manager : WorkspaceManager, optional
|
|
1345
|
+
The WorkspaceManager the StarterWorkspace belongs to
|
|
1346
|
+
|
|
1347
|
+
Returns
|
|
1348
|
+
-------
|
|
1349
|
+
:class:`StarterWorkspace`
|
|
1350
|
+
|
|
1351
|
+
"""
|
|
1352
|
+
out = cls(
|
|
1353
|
+
name=obj['name'],
|
|
1354
|
+
id=obj['virtualWorkspaceID'],
|
|
1355
|
+
)
|
|
1356
|
+
out._manager = manager
|
|
1357
|
+
return out
|
|
1358
|
+
|
|
1359
|
+
@property
|
|
1360
|
+
def organization(self) -> Organization:
|
|
1361
|
+
if self._manager is None:
|
|
1362
|
+
raise ManagementError(
|
|
1363
|
+
msg='No workspace manager is associated with this object.',
|
|
1364
|
+
)
|
|
1365
|
+
return self._manager.organization
|
|
1366
|
+
|
|
1367
|
+
@property
|
|
1368
|
+
def stage(self) -> Stage:
|
|
1369
|
+
"""Stage manager."""
|
|
1370
|
+
if self._manager is None:
|
|
1371
|
+
raise ManagementError(
|
|
1372
|
+
msg='No workspace manager is associated with this object.',
|
|
1373
|
+
)
|
|
1374
|
+
return Stage(self.id, self._manager)
|
|
1375
|
+
|
|
1376
|
+
stages = stage
|
|
1377
|
+
|
|
1378
|
+
@property
|
|
1379
|
+
def starter_workspaces(self) -> NamedList[StarterWorkspace]:
|
|
1380
|
+
"""Return a list of available starter workspaces."""
|
|
1381
|
+
if self._manager is None:
|
|
1382
|
+
raise ManagementError(
|
|
1383
|
+
msg='No workspace manager is associated with this object.',
|
|
1384
|
+
)
|
|
1385
|
+
res = self._manager._get('sharedtier/virtualWorkspaces')
|
|
1386
|
+
return NamedList(
|
|
1387
|
+
[StarterWorkspace.from_dict(item, self._manager) for item in res.json()],
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1390
|
+
|
|
1601
1391
|
class Billing(object):
|
|
1602
1392
|
"""Billing information."""
|
|
1603
1393
|
|
|
@@ -1705,6 +1495,12 @@ class WorkspaceManager(Manager):
|
|
|
1705
1495
|
res = self._get('workspaceGroups')
|
|
1706
1496
|
return NamedList([WorkspaceGroup.from_dict(item, self) for item in res.json()])
|
|
1707
1497
|
|
|
1498
|
+
@property
|
|
1499
|
+
def starter_workspaces(self) -> NamedList[StarterWorkspace]:
|
|
1500
|
+
"""Return a list of available starter workspaces."""
|
|
1501
|
+
res = self._get('sharedtier/virtualWorkspaces')
|
|
1502
|
+
return NamedList([StarterWorkspace.from_dict(item, self) for item in res.json()])
|
|
1503
|
+
|
|
1708
1504
|
@property
|
|
1709
1505
|
def organizations(self) -> Organizations:
|
|
1710
1506
|
"""Return the organizations."""
|
|
@@ -1904,6 +1700,23 @@ class WorkspaceManager(Manager):
|
|
|
1904
1700
|
res = self._get(f'workspaces/{id}')
|
|
1905
1701
|
return Workspace.from_dict(res.json(), manager=self)
|
|
1906
1702
|
|
|
1703
|
+
def get_starter_workspace(self, id: str) -> StarterWorkspace:
|
|
1704
|
+
"""
|
|
1705
|
+
Retrieve a starter workspace definition.
|
|
1706
|
+
|
|
1707
|
+
Parameters
|
|
1708
|
+
----------
|
|
1709
|
+
id : str
|
|
1710
|
+
ID of the starter workspace
|
|
1711
|
+
|
|
1712
|
+
Returns
|
|
1713
|
+
-------
|
|
1714
|
+
:class:`StarterWorkspace`
|
|
1715
|
+
|
|
1716
|
+
"""
|
|
1717
|
+
res = self._get(f'sharedtier/virtualWorkspaces/{id}')
|
|
1718
|
+
return StarterWorkspace.from_dict(res.json(), manager=self)
|
|
1719
|
+
|
|
1907
1720
|
|
|
1908
1721
|
def manage_workspaces(
|
|
1909
1722
|
access_token: Optional[str] = None,
|