NGPIris 5.6.3.dev1__tar.gz → 5.6.5__tar.gz
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.
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/cli/__init__.py +74 -38
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/cli/helpers.py +26 -9
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hcp/exceptions.py +33 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hcp/hcp.py +179 -152
- ngpiris-5.6.5/NGPIris/hcp/helpers.py +178 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hcp/statistics.py +5 -5
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/PKG-INFO +4 -6
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/requires.txt +1 -1
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/PKG-INFO +4 -6
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/README.md +2 -4
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/pyproject.toml +3 -3
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/test_hcp.py +5 -5
- ngpiris-5.6.3.dev1/NGPIris/hcp/helpers.py +0 -70
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/LICENSE +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/MANIFEST.in +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/__init__.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/cli/sections.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hci/__init__.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hci/exceptions.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hci/hci.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hci/helpers.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/hcp/__init__.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/parse_credentials/__init__.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/parse_credentials/parse_credentials.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/utils/__init__.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris/utils/utils.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/SOURCES.txt +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/dependency_links.txt +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/entry_points.txt +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/NGPIris.egg-info/top_level.txt +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/IRIS_5_changelog.md +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/Makefile +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.cli.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.hci.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.hcp.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.parse_credentials.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/NGPIris.utils.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/Tutorial.md +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/conf.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/index.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/make.bat +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/docs/modules.rst +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/setup.cfg +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/80MB_test_file +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/a folder of data/80MB_test_file_0 +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/a folder of data/80MB_test_file_1 +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/complete_creds_0.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/complete_creds_1.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/complete_creds_2.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/incomplete_creds_0.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/incomplete_creds_1.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/incomplete_creds_2.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/incomplete_creds_3.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/data/incomplete_creds_4.json +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/test_hci.py +0 -0
- {ngpiris-5.6.3.dev1 → ngpiris-5.6.5}/tests/test_parse_credentials.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
# ruff: noqa: SLF001
|
|
2
|
+
|
|
1
3
|
import sys
|
|
2
|
-
from collections.abc import Generator
|
|
3
4
|
from json import dump
|
|
4
5
|
from pathlib import Path
|
|
5
|
-
from typing import Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
6
7
|
|
|
7
8
|
import click
|
|
8
|
-
import
|
|
9
|
-
from click.core import Context
|
|
9
|
+
from bitmath import SI, Byte
|
|
10
10
|
from tabulate import tabulate
|
|
11
11
|
|
|
12
12
|
from NGPIris import HCPHandler
|
|
@@ -16,10 +16,19 @@ from NGPIris.cli.helpers import (
|
|
|
16
16
|
download_file,
|
|
17
17
|
download_folder,
|
|
18
18
|
ensure_destination_dir,
|
|
19
|
-
|
|
19
|
+
render_objects_table,
|
|
20
20
|
)
|
|
21
21
|
from NGPIris.cli.sections import SectionedGroup
|
|
22
|
-
from NGPIris.hcp.exceptions import
|
|
22
|
+
from NGPIris.hcp.exceptions import (
|
|
23
|
+
IsFileObjectError,
|
|
24
|
+
IsFolderObjectError,
|
|
25
|
+
ObjectDoesNotExistError,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import Generator
|
|
30
|
+
|
|
31
|
+
from click.core import Context
|
|
23
32
|
|
|
24
33
|
|
|
25
34
|
@click.group(cls=SectionedGroup)
|
|
@@ -244,12 +253,12 @@ def delete(
|
|
|
244
253
|
+ '", the following file objects would have been deleted '
|
|
245
254
|
+ "(this list excludes any potential sub-folders):",
|
|
246
255
|
)
|
|
247
|
-
|
|
256
|
+
render_objects_table(
|
|
248
257
|
hcp_h.list_objects(
|
|
249
258
|
hcp_object,
|
|
250
259
|
files_only=True,
|
|
251
260
|
),
|
|
252
|
-
|
|
261
|
+
-1,
|
|
253
262
|
)
|
|
254
263
|
|
|
255
264
|
|
|
@@ -308,26 +317,21 @@ def download( # noqa: PLR0913
|
|
|
308
317
|
|
|
309
318
|
destination_path = ensure_destination_dir(destination)
|
|
310
319
|
|
|
311
|
-
is_folder =
|
|
320
|
+
is_folder = hcp_h._is_object_folder(source)
|
|
312
321
|
|
|
313
322
|
if dry_run:
|
|
314
|
-
if
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)
|
|
322
|
-
else:
|
|
323
|
-
click.echo(
|
|
324
|
-
'This command would have downloaded the object "'
|
|
325
|
-
+ source
|
|
326
|
-
+ '"',
|
|
327
|
-
)
|
|
323
|
+
if is_folder:
|
|
324
|
+
click.echo(
|
|
325
|
+
'This command would have downloaded the folder "'
|
|
326
|
+
+ source
|
|
327
|
+
+ '". If you wish to know the contents of this folder, '
|
|
328
|
+
+ "use the 'list-objects' command",
|
|
329
|
+
)
|
|
328
330
|
else:
|
|
329
331
|
click.echo(
|
|
330
|
-
'
|
|
332
|
+
'This command would have downloaded the object "'
|
|
333
|
+
+ source
|
|
334
|
+
+ '"',
|
|
331
335
|
)
|
|
332
336
|
return
|
|
333
337
|
|
|
@@ -343,6 +347,14 @@ def download( # noqa: PLR0913
|
|
|
343
347
|
)
|
|
344
348
|
@click.argument("bucket")
|
|
345
349
|
@click.argument("path", required=False)
|
|
350
|
+
@click.option(
|
|
351
|
+
"-bs",
|
|
352
|
+
"--batch_size",
|
|
353
|
+
help="Number of rows added to the output at a time. If value is zero or a "
|
|
354
|
+
"negative number, all rows will be displayed in one go. "
|
|
355
|
+
"Default value is 25",
|
|
356
|
+
default=25,
|
|
357
|
+
)
|
|
346
358
|
@click.option(
|
|
347
359
|
"-p",
|
|
348
360
|
"--pagination",
|
|
@@ -369,6 +381,7 @@ def list_objects( # noqa: PLR0913
|
|
|
369
381
|
context: Context,
|
|
370
382
|
bucket: str,
|
|
371
383
|
path: str,
|
|
384
|
+
batch_size: int,
|
|
372
385
|
pagination: bool,
|
|
373
386
|
files_only: bool,
|
|
374
387
|
extended_information: bool,
|
|
@@ -381,12 +394,12 @@ def list_objects( # noqa: PLR0913
|
|
|
381
394
|
PATH is an optional argument for where to list the objects
|
|
382
395
|
"""
|
|
383
396
|
|
|
384
|
-
def
|
|
397
|
+
def list_objects_pagination_generator(
|
|
385
398
|
hcp_h: HCPHandler,
|
|
386
399
|
path: str,
|
|
387
|
-
files_only: bool,
|
|
388
400
|
output_mode: HCPHandler.ListObjectsOutputMode,
|
|
389
|
-
|
|
401
|
+
files_only: bool,
|
|
402
|
+
) -> Generator[str, Any]:
|
|
390
403
|
"""
|
|
391
404
|
Handle object list as a paginator that `click` can handle.
|
|
392
405
|
It works slightly different from `list_objects` in `hcp.py` in order to
|
|
@@ -400,6 +413,24 @@ def list_objects( # noqa: PLR0913
|
|
|
400
413
|
for obj in objects:
|
|
401
414
|
yield str(obj) + "\n"
|
|
402
415
|
|
|
416
|
+
def list_objects_generator(
|
|
417
|
+
hcp_h: HCPHandler,
|
|
418
|
+
path: str,
|
|
419
|
+
output_mode: HCPHandler.ListObjectsOutputMode,
|
|
420
|
+
files_only: bool,
|
|
421
|
+
) -> Generator[dict[str, Any], Any]:
|
|
422
|
+
objects = hcp_h.list_objects(
|
|
423
|
+
path,
|
|
424
|
+
output_mode=output_mode,
|
|
425
|
+
files_only=files_only,
|
|
426
|
+
)
|
|
427
|
+
for obj in objects:
|
|
428
|
+
if obj.get("Size", 0):
|
|
429
|
+
obj["Size"] = str(
|
|
430
|
+
Byte(obj.get("Size", 0)).best_prefix(system=SI)
|
|
431
|
+
)
|
|
432
|
+
yield obj
|
|
433
|
+
|
|
403
434
|
hcp_h: HCPHandler = create_HCPHandler(context)
|
|
404
435
|
hcp_h.mount_bucket(bucket)
|
|
405
436
|
output_mode = (
|
|
@@ -408,6 +439,10 @@ def list_objects( # noqa: PLR0913
|
|
|
408
439
|
else HCPHandler.ListObjectsOutputMode.SIMPLE
|
|
409
440
|
)
|
|
410
441
|
|
|
442
|
+
if not hcp_h._is_object_folder(path):
|
|
443
|
+
msg = "The provided path is a file, which is not a valid path"
|
|
444
|
+
raise IsFileObjectError(msg)
|
|
445
|
+
|
|
411
446
|
if path:
|
|
412
447
|
path_with_slash = add_trailing_slash(path)
|
|
413
448
|
|
|
@@ -419,21 +454,22 @@ def list_objects( # noqa: PLR0913
|
|
|
419
454
|
|
|
420
455
|
if pagination:
|
|
421
456
|
click.echo_via_pager(
|
|
422
|
-
|
|
457
|
+
list_objects_pagination_generator(
|
|
423
458
|
hcp_h,
|
|
424
459
|
path_with_slash,
|
|
425
|
-
files_only,
|
|
426
460
|
output_mode,
|
|
461
|
+
files_only,
|
|
427
462
|
),
|
|
428
463
|
)
|
|
429
464
|
else:
|
|
430
|
-
|
|
431
|
-
|
|
465
|
+
render_objects_table(
|
|
466
|
+
list_objects_generator(
|
|
467
|
+
hcp_h,
|
|
432
468
|
path_with_slash,
|
|
433
|
-
output_mode
|
|
434
|
-
files_only
|
|
469
|
+
output_mode,
|
|
470
|
+
files_only,
|
|
435
471
|
),
|
|
436
|
-
|
|
472
|
+
batch_size,
|
|
437
473
|
)
|
|
438
474
|
|
|
439
475
|
|
|
@@ -689,9 +725,9 @@ def simple_search(
|
|
|
689
725
|
case_sensitive=case_sensitive,
|
|
690
726
|
)
|
|
691
727
|
click.echo("Search results:")
|
|
692
|
-
|
|
728
|
+
render_objects_table(
|
|
693
729
|
list_of_results,
|
|
694
|
-
|
|
730
|
+
-1,
|
|
695
731
|
)
|
|
696
732
|
|
|
697
733
|
|
|
@@ -744,9 +780,9 @@ def fuzzy_search(
|
|
|
744
780
|
threshold=threshold,
|
|
745
781
|
)
|
|
746
782
|
click.echo("Search results:")
|
|
747
|
-
|
|
783
|
+
render_objects_table(
|
|
748
784
|
list_of_results,
|
|
749
|
-
|
|
785
|
+
-1,
|
|
750
786
|
)
|
|
751
787
|
|
|
752
788
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from typing import Any, Generator
|
|
4
5
|
|
|
5
6
|
import click
|
|
6
7
|
from bitmath import Byte, TiB
|
|
7
8
|
from boto3 import set_stream_logger
|
|
8
9
|
from click.core import Context
|
|
10
|
+
from tabulate import tabulate
|
|
9
11
|
|
|
10
12
|
from NGPIris import HCPHandler
|
|
11
13
|
|
|
@@ -89,15 +91,6 @@ def create_HCPHandler(context: Context) -> HCPHandler:
|
|
|
89
91
|
return hcp_h
|
|
90
92
|
|
|
91
93
|
|
|
92
|
-
def object_is_folder(object_path: str, hcp_h: HCPHandler) -> bool:
|
|
93
|
-
"""
|
|
94
|
-
Predicate for checking if an HCP object is a folder or not.
|
|
95
|
-
"""
|
|
96
|
-
return (object_path.endswith("/")) and (
|
|
97
|
-
hcp_h.get_object(object_path)["ContentLength"] == 0
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
|
|
101
94
|
def ensure_destination_dir(destination: str) -> Path:
|
|
102
95
|
"""
|
|
103
96
|
Ensure that `destination` exists and return it as a `Path`.
|
|
@@ -161,3 +154,27 @@ def download_file(
|
|
|
161
154
|
" use the -f / --force option"
|
|
162
155
|
)
|
|
163
156
|
hcp_h.download_file(source, downloaded_source.as_posix())
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def render_objects_table(
|
|
160
|
+
table_data: Generator[dict[str, Any], Any, None], batch_size: int
|
|
161
|
+
) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Helper function for lazily displaying objects in a table with a given batch
|
|
164
|
+
size.
|
|
165
|
+
"""
|
|
166
|
+
rows = []
|
|
167
|
+
positive_batch_size = batch_size > 0
|
|
168
|
+
for row in table_data:
|
|
169
|
+
rows.append(row)
|
|
170
|
+
if positive_batch_size and (
|
|
171
|
+
not len(rows) % batch_size
|
|
172
|
+
): # Check if `len(rows)` is a mutliple of `batch_size`
|
|
173
|
+
click.echo(tabulate(rows, headers="keys"))
|
|
174
|
+
click.echo(
|
|
175
|
+
"(Press any key to get more rows or ctrl+c to abort...)",
|
|
176
|
+
nl=False,
|
|
177
|
+
)
|
|
178
|
+
click.getchar()
|
|
179
|
+
click.clear()
|
|
180
|
+
click.echo(tabulate(rows, headers="keys"))
|
|
@@ -19,11 +19,38 @@ class NotSufficientPermissionsError(Exception):
|
|
|
19
19
|
The given permissions where not sufficient when making a MAPI request (403).
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
+
|
|
22
23
|
class NotFoundError(Exception):
|
|
23
24
|
"""
|
|
24
25
|
The given request path was not found when making a MAPI request (404).
|
|
25
26
|
"""
|
|
26
27
|
|
|
28
|
+
|
|
29
|
+
# -------------- Operation response exceptions --------------
|
|
30
|
+
class MetadataCouldNotBeFoundError(Exception):
|
|
31
|
+
"""
|
|
32
|
+
The response metadata could not be found.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class OperationNotPermittedError(Exception):
|
|
37
|
+
"""
|
|
38
|
+
The user is not permitted to do that operation on this bucket (403).
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class NoStatusCodeSuppliedError(Exception):
|
|
43
|
+
"""
|
|
44
|
+
`HTTPStatusCode` in the response metadata was not supplied.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class UnknownStatusCodeError(Exception):
|
|
49
|
+
"""
|
|
50
|
+
The status code recieved is not known.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
|
|
27
54
|
# -------------- Bucket exceptions --------------
|
|
28
55
|
class NoBucketMountedError(Exception):
|
|
29
56
|
"""
|
|
@@ -64,6 +91,12 @@ class IsFolderObjectError(Exception):
|
|
|
64
91
|
"""
|
|
65
92
|
|
|
66
93
|
|
|
94
|
+
class IsFileObjectError(Exception):
|
|
95
|
+
"""
|
|
96
|
+
The object on the mounted bucket is a file.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
|
|
67
100
|
class SubfolderError(Exception):
|
|
68
101
|
"""
|
|
69
102
|
There is at least one subfolder in the given path on the mounted bucket.
|