Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
singlestore-labs
GitHub Repository: singlestore-labs/singlestoredb-python
Path: blob/main/singlestoredb/fusion/handlers/files.py
801 views
1
#!/usr/bin/env python3
2
from typing import Any
3
from typing import Dict
4
from typing import Optional
5
6
from .. import result
7
from ..handler import SQLHandler
8
from ..result import FusionSQLResult
9
from .utils import dt_isoformat
10
from .utils import get_file_space
11
12
13
class ShowFilesHandler(SQLHandler):
14
"""
15
Generic handler for listing files in a personal/shared space.
16
""" # noqa: E501
17
18
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
19
file_space = get_file_space(params)
20
21
res = FusionSQLResult()
22
res.add_field('Name', result.STRING)
23
24
if params['extended']:
25
res.add_field('Type', result.STRING)
26
res.add_field('Size', result.INTEGER)
27
res.add_field('Writable', result.STRING)
28
res.add_field('CreatedAt', result.DATETIME)
29
res.add_field('LastModifiedAt', result.DATETIME)
30
31
files = []
32
for x in file_space.listdir(
33
params['at_path'] or '/',
34
recursive=params['recursive'],
35
return_objects=False,
36
):
37
info = file_space.info(x)
38
files.append(
39
tuple([
40
x, info.type, info.size or 0, info.writable,
41
dt_isoformat(info.created_at),
42
dt_isoformat(info.last_modified_at),
43
]),
44
)
45
res.set_rows(files)
46
47
else:
48
res.set_rows([(x,) for x in file_space.listdir(
49
params['at_path'] or '/',
50
recursive=params['recursive'],
51
return_objects=False,
52
)])
53
54
if params['like']:
55
res = res.like(Name=params['like'])
56
57
return res.order_by(**params['order_by']).limit(params['limit'])
58
59
60
class ShowPersonalFilesHandler(ShowFilesHandler):
61
"""
62
SHOW PERSONAL FILES
63
[ at_path ] [ <like> ]
64
[ <order-by> ]
65
[ <limit> ] [ recursive ] [ extended ];
66
67
# File path to list
68
at_path = AT '<path>'
69
70
# Should the listing be recursive?
71
recursive = RECURSIVE
72
73
# Should extended attributes be shown?
74
extended = EXTENDED
75
76
Description
77
-----------
78
Displays a list of files in a personal/shared space.
79
80
Arguments
81
---------
82
* ``<path>``: A path in the personal/shared space.
83
* ``<pattern>``: A pattern similar to SQL LIKE clause.
84
Uses ``%`` as the wildcard character.
85
86
Remarks
87
-------
88
* Use the ``LIKE`` clause to specify a pattern and return only the
89
files that match the specified pattern.
90
* The ``LIMIT`` clause limits the number of results to the
91
specified number.
92
* Use the ``ORDER BY`` clause to sort the results by the specified
93
key. By default, the results are sorted in the ascending order.
94
* The ``AT`` clause specifies the path in the personal/shared
95
space to list the files from.
96
* Use the ``RECURSIVE`` clause to list the files recursively.
97
* To return more information about the files, use the ``EXTENDED``
98
clause.
99
100
Examples
101
--------
102
The following command lists the files at a specific path::
103
104
SHOW PERSONAL FILES AT "/data/";
105
106
The following command lists the files recursively with
107
additional information::
108
109
SHOW PERSONAL FILES RECURSIVE EXTENDED;
110
111
See Also
112
--------
113
* ``SHOW SHARED FILES``
114
* ``UPLOAD PERSONAL FILE``
115
* ``UPLOAD SHARED FILE``
116
* ``DOWNLOAD PERSONAL FILE``
117
* ``DOWNLOAD SHARED FILE``
118
119
""" # noqa: E501
120
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
121
params['file_location'] = 'PERSONAL'
122
return super().run(params)
123
124
125
class ShowSharedFilesHandler(ShowFilesHandler):
126
"""
127
SHOW SHARED FILES
128
[ at_path ] [ <like> ]
129
[ <order-by> ]
130
[ <limit> ] [ recursive ] [ extended ];
131
132
# File path to list
133
at_path = AT '<path>'
134
135
# Should the listing be recursive?
136
recursive = RECURSIVE
137
138
# Should extended attributes be shown?
139
extended = EXTENDED
140
141
Description
142
-----------
143
Displays a list of files in a personal/shared space.
144
145
Arguments
146
---------
147
* ``<path>``: A path in the personal/shared space.
148
* ``<pattern>``: A pattern similar to SQL LIKE clause.
149
Uses ``%`` as the wildcard character.
150
151
Remarks
152
-------
153
* Use the ``LIKE`` clause to specify a pattern and return only the
154
files that match the specified pattern.
155
* The ``LIMIT`` clause limits the number of results to the
156
specified number.
157
* Use the ``ORDER BY`` clause to sort the results by the specified
158
key. By default, the results are sorted in the ascending order.
159
* The ``AT`` clause specifies the path in the personal/shared
160
space to list the files from.
161
* Use the ``RECURSIVE`` clause to list the files recursively.
162
* To return more information about the files, use the ``EXTENDED``
163
clause.
164
165
Examples
166
--------
167
The following command lists the files at a specific path::
168
169
SHOW SHARED FILES AT "/data/";
170
171
The following command lists the files recursively with
172
additional information::
173
174
SHOW SHARED FILES RECURSIVE EXTENDED;
175
176
See Also
177
--------
178
* ``SHOW PERSONAL FILES``
179
* ``UPLOAD PERSONAL FILE``
180
* ``UPLOAD SHARED FILE``
181
* ``DOWNLOAD PERSONAL FILE``
182
* ``DOWNLOAD SHARED FILE``
183
184
""" # noqa: E501
185
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
186
params['file_location'] = 'SHARED'
187
return super().run(params)
188
189
190
ShowPersonalFilesHandler.register(overwrite=True)
191
ShowSharedFilesHandler.register(overwrite=True)
192
193
194
class UploadFileHandler(SQLHandler):
195
"""
196
Generic handler for uploading files to a personal/shared space.
197
""" # noqa: E501
198
199
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
200
file_space = get_file_space(params)
201
file_space.upload_file(
202
params['local_path'], params['path'],
203
overwrite=params['overwrite'],
204
)
205
return None
206
207
208
class UploadPersonalFileHandler(UploadFileHandler):
209
"""
210
UPLOAD PERSONAL FILE TO path
211
FROM local_path [ overwrite ];
212
213
# Path to file
214
path = '<filename>'
215
216
# Path to local file
217
local_path = '<local-path>'
218
219
# Should an existing file be overwritten?
220
overwrite = OVERWRITE
221
222
Description
223
-----------
224
Uploads a file to a personal/shared space.
225
226
Arguments
227
---------
228
* ``<filename>``: The filename in the personal/shared space where the file is uploaded.
229
* ``<local-path>``: The path to the file to upload in the local
230
directory.
231
232
Remarks
233
-------
234
* If the ``OVERWRITE`` clause is specified, any existing file at the
235
specified path in the personal/shared space is overwritten.
236
237
Examples
238
--------
239
The following command uploads a file to a personal/shared space and overwrite any
240
existing files at the specified path::
241
242
UPLOAD PERSONAL FILE TO 'stats.csv'
243
FROM '/tmp/user/stats.csv' OVERWRITE;
244
245
See Also
246
--------
247
* ``UPLOAD SHARED FILE``
248
* ``DOWNLOAD PERSONAL FILE``
249
* ``DOWNLOAD SHARED FILE``
250
251
""" # noqa: E501
252
253
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
254
params['file_location'] = 'PERSONAL'
255
return super().run(params)
256
257
258
class UploadSharedFileHandler(UploadFileHandler):
259
"""
260
UPLOAD SHARED FILE TO path
261
FROM local_path [ overwrite ];
262
263
# Path to file
264
path = '<filename>'
265
266
# Path to local file
267
local_path = '<local-path>'
268
269
# Should an existing file be overwritten?
270
overwrite = OVERWRITE
271
272
Description
273
-----------
274
Uploads a file to a personal/shared space.
275
276
Arguments
277
---------
278
* ``<filename>``: The filename in the personal/shared space where the file is uploaded.
279
* ``<local-path>``: The path to the file to upload in the local
280
directory.
281
282
Remarks
283
-------
284
* If the ``OVERWRITE`` clause is specified, any existing file at the
285
specified path in the personal/shared space is overwritten.
286
287
Examples
288
--------
289
The following command uploads a file to a personal/shared space and overwrite any
290
existing files at the specified path::
291
292
UPLOAD SHARED FILE TO 'stats.csv'
293
FROM '/tmp/user/stats.csv' OVERWRITE;
294
295
See Also
296
--------
297
* ``UPLOAD PERSONAL FILE``
298
* ``DOWNLOAD PERSONAL FILE``
299
* ``DOWNLOAD SHARED FILE``
300
301
""" # noqa: E501
302
303
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
304
params['file_location'] = 'SHARED'
305
return super().run(params)
306
307
308
UploadPersonalFileHandler.register(overwrite=True)
309
UploadSharedFileHandler.register(overwrite=True)
310
311
312
class DownloadFileHandler(SQLHandler):
313
"""
314
Generic handler for downloading files from a personal/shared space.
315
""" # noqa: E501
316
317
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
318
file_space = get_file_space(params)
319
320
out = file_space.download_file(
321
params['path'],
322
local_path=params['local_path'] or None,
323
overwrite=params['overwrite'],
324
encoding=params['encoding'] or None,
325
)
326
327
if not params['local_path']:
328
res = FusionSQLResult()
329
if params['encoding']:
330
res.add_field('Data', result.STRING)
331
else:
332
res.add_field('Data', result.BLOB)
333
res.set_rows([(out,)])
334
return res
335
336
return None
337
338
339
class DownloadPersonalFileHandler(DownloadFileHandler):
340
"""
341
DOWNLOAD PERSONAL FILE path
342
[ local_path ]
343
[ overwrite ]
344
[ encoding ];
345
346
# Path to file
347
path = '<path>'
348
349
# Path to local file
350
local_path = TO '<local-path>'
351
352
# Should an existing file be overwritten?
353
overwrite = OVERWRITE
354
355
# File encoding
356
encoding = ENCODING '<encoding>'
357
358
Description
359
-----------
360
Download a file from a personal/shared space.
361
362
Arguments
363
---------
364
* ``<path>``: The path to the file to download in a personal/shared space.
365
* ``<encoding>``: The encoding to apply to the downloaded file.
366
* ``<local-path>``: Specifies the path in the local directory
367
where the file is downloaded.
368
369
Remarks
370
-------
371
* If the ``OVERWRITE`` clause is specified, any existing file at
372
the download location is overwritten.
373
* By default, files are downloaded in binary encoding. To view
374
the contents of the file on the standard output, use the
375
``ENCODING`` clause and specify an encoding.
376
* If ``<local-path>`` is not specified, the file is displayed
377
on the standard output.
378
379
Examples
380
--------
381
The following command displays the contents of the file on the
382
standard output::
383
384
DOWNLOAD PERSONAL FILE '/data/stats.csv' ENCODING 'utf8';
385
386
The following command downloads a file to a specific location and
387
overwrites any existing file with the name ``stats.csv`` on the local storage::
388
389
DOWNLOAD PERSONAL FILE '/data/stats.csv'
390
TO '/tmp/data.csv' OVERWRITE;
391
392
See Also
393
--------
394
* ``DOWNLOAD SHARED FILE``
395
* ``UPLOAD PERSONAL FILE``
396
* ``UPLOAD SHARED FILE``
397
398
""" # noqa: E501
399
400
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
401
params['file_location'] = 'PERSONAL'
402
return super().run(params)
403
404
405
class DownloadSharedFileHandler(DownloadFileHandler):
406
"""
407
DOWNLOAD SHARED FILE path
408
[ local_path ]
409
[ overwrite ]
410
[ encoding ];
411
412
# Path to file
413
path = '<path>'
414
415
# Path to local file
416
local_path = TO '<local-path>'
417
418
# Should an existing file be overwritten?
419
overwrite = OVERWRITE
420
421
# File encoding
422
encoding = ENCODING '<encoding>'
423
424
Description
425
-----------
426
Download a file from a personal/shared space.
427
428
Arguments
429
---------
430
* ``<path>``: The path to the file to download in a personal/shared space.
431
* ``<encoding>``: The encoding to apply to the downloaded file.
432
* ``<local-path>``: Specifies the path in the local directory
433
where the file is downloaded.
434
435
Remarks
436
-------
437
* If the ``OVERWRITE`` clause is specified, any existing file at
438
the download location is overwritten.
439
* By default, files are downloaded in binary encoding. To view
440
the contents of the file on the standard output, use the
441
``ENCODING`` clause and specify an encoding.
442
* If ``<local-path>`` is not specified, the file is displayed
443
on the standard output.
444
445
Examples
446
--------
447
The following command displays the contents of the file on the
448
standard output::
449
450
DOWNLOAD SHARED FILE '/data/stats.csv' ENCODING 'utf8';
451
452
The following command downloads a file to a specific location and
453
overwrites any existing file with the name ``stats.csv`` on the local storage::
454
455
DOWNLOAD SHARED FILE '/data/stats.csv'
456
TO '/tmp/data.csv' OVERWRITE;
457
458
See Also
459
--------
460
* ``DOWNLOAD PERSONAL FILE``
461
* ``UPLOAD PERSONAL FILE``
462
* ``UPLOAD SHARED FILE``
463
464
""" # noqa: E501
465
466
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
467
params['file_location'] = 'SHARED'
468
return super().run(params)
469
470
471
DownloadPersonalFileHandler.register(overwrite=True)
472
DownloadSharedFileHandler.register(overwrite=True)
473
474
475
class DropHandler(SQLHandler):
476
"""
477
Generic handler for deleting files/folders from a personal/shared space.
478
""" # noqa: E501
479
480
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
481
file_space = get_file_space(params)
482
483
file_type = params['file_type']
484
if not file_type:
485
raise KeyError('file type was not specified')
486
487
file_type = file_type.lower()
488
if file_type not in ['file', 'folder']:
489
raise ValueError('file type must be either FILE or FOLDER')
490
491
if file_type == 'file':
492
file_space.remove(params['path'])
493
elif file_type == 'folder':
494
if params['recursive']:
495
file_space.removedirs(params['path'])
496
else:
497
file_space.rmdir(params['path'])
498
499
return None
500
501
502
class DropPersonalHandler(DropHandler):
503
"""
504
DROP PERSONAL <file-type> path
505
[ recursive ];
506
507
# Path to file
508
path = '<path>'
509
510
# Should folders be deleted recursively?
511
recursive = RECURSIVE
512
513
Description
514
-----------
515
Deletes a file/folder from a personal/shared space.
516
517
Arguments
518
---------
519
* ``<file-type>``: The type of the file, it can
520
be either 'FILE' or 'FOLDER'.
521
* ``<path>``: The path to the file to delete in a personal/shared space.
522
523
Remarks
524
-------
525
* The ``RECURSIVE`` clause indicates that the specified folder
526
is deleted recursively.
527
528
Example
529
--------
530
The following commands delete a file/folder from a personal/shared space::
531
532
DROP PERSONAL FILE '/data/stats.csv';
533
DROP PERSONAL FOLDER '/data/' RECURSIVE;
534
535
See Also
536
--------
537
* ``DROP SHARED FILE``
538
* ``DROP SHARED FOLDER``
539
540
""" # noqa: E501
541
542
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
543
params['file_location'] = 'PERSONAL'
544
return super().run(params)
545
546
547
class DropSharedHandler(DropHandler):
548
"""
549
DROP SHARED <file-type> path
550
[ recursive ];
551
552
# Path to file
553
path = '<path>'
554
555
# Should folders be deleted recursively?
556
recursive = RECURSIVE
557
558
Description
559
-----------
560
Deletes a file/folder from a personal/shared space.
561
562
Arguments
563
---------
564
* ``<file-type>``: The type of the file, it can
565
be either 'FILE' or 'FOLDER'.
566
* ``<path>``: The path to the file to delete in a personal/shared space.
567
568
Remarks
569
-------
570
* The ``RECURSIVE`` clause indicates that the specified folder
571
is deleted recursively.
572
573
Example
574
--------
575
The following commands delete a file/folder from a personal/shared space::
576
577
DROP SHARED FILE '/data/stats.csv';
578
DROP SHARED FOLDER '/data/' RECURSIVE;
579
580
See Also
581
--------
582
* ``DROP PERSONAL FILE``
583
* ``DROP PERSONAL FOLDER``
584
585
""" # noqa: E501
586
587
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
588
params['file_location'] = 'SHARED'
589
return super().run(params)
590
591
592
DropPersonalHandler.register(overwrite=True)
593
DropSharedHandler.register(overwrite=True)
594
595
596
class CreateFolderHandler(SQLHandler):
597
"""
598
Generic handler for creating folders in a personal/shared space.
599
"""
600
601
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
602
file_space = get_file_space(params)
603
file_space.mkdir(params['path'], overwrite=params['overwrite'])
604
return None
605
606
607
class CreatePersonalFolderHandler(CreateFolderHandler):
608
"""
609
CREATE PERSONAL FOLDER path
610
[ overwrite ];
611
612
# Path to folder
613
path = '<path>'
614
615
# Should an existing folder be overwritten?
616
overwrite = OVERWRITE
617
618
Description
619
-----------
620
Creates a new folder at the specified path in a personal/shared space.
621
622
Arguments
623
---------
624
* ``<path>``: The path in a personal/shared space where the folder
625
is created. The path must end with a trailing slash (/).
626
627
Remarks
628
-------
629
* If the ``OVERWRITE`` clause is specified, any existing
630
folder at the specified path is overwritten.
631
632
Example
633
-------
634
The following command creates a folder in a personal/shared space::
635
636
CREATE PERSONAL FOLDER `/data/csv/`;
637
638
See Also
639
--------
640
* ``CREATE SHARED FOLDER``
641
642
"""
643
644
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
645
params['file_location'] = 'PERSONAL'
646
return super().run(params)
647
648
649
class CreateSharedFolderHandler(CreateFolderHandler):
650
"""
651
CREATE SHARED FOLDER path
652
[ overwrite ];
653
654
# Path to folder
655
path = '<path>'
656
657
# Should an existing folder be overwritten?
658
overwrite = OVERWRITE
659
660
Description
661
-----------
662
Creates a new folder at the specified path in a personal/shared space.
663
664
Arguments
665
---------
666
* ``<path>``: The path in a personal/shared space where the folder
667
is created. The path must end with a trailing slash (/).
668
669
Remarks
670
-------
671
* If the ``OVERWRITE`` clause is specified, any existing
672
folder at the specified path is overwritten.
673
674
Example
675
-------
676
The following command creates a folder in a personal/shared space::
677
678
CREATE SHARED FOLDER `/data/csv/`;
679
680
See Also
681
--------
682
* ``CREATE PERSONAL FOLDER``
683
684
"""
685
686
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
687
params['file_location'] = 'SHARED'
688
return super().run(params)
689
690
691
CreatePersonalFolderHandler.register(overwrite=True)
692
CreateSharedFolderHandler.register(overwrite=True)
693
694