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