NGPIris 5.3.1__tar.gz → 5.4.1__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.3.1 → ngpiris-5.4.1}/NGPIris/cli/__init__.py +4 -35
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hcp/hcp.py +67 -36
- {ngpiris-5.3.1 → ngpiris-5.4.1/NGPIris.egg-info}/PKG-INFO +3 -2
- {ngpiris-5.3.1/NGPIris.egg-info → ngpiris-5.4.1}/PKG-INFO +3 -2
- {ngpiris-5.3.1 → ngpiris-5.4.1}/pyproject.toml +1 -1
- {ngpiris-5.3.1 → ngpiris-5.4.1}/LICENSE +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/MANIFEST.in +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/__init__.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hci/__init__.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hci/exceptions.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hci/hci.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hci/helpers.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hcp/__init__.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hcp/exceptions.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hcp/helpers.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/hcp/statistics.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/parse_credentials/__init__.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/parse_credentials/parse_credentials.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/utils/__init__.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris/utils/utils.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris.egg-info/SOURCES.txt +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris.egg-info/dependency_links.txt +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris.egg-info/entry_points.txt +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris.egg-info/requires.txt +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/NGPIris.egg-info/top_level.txt +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/README.md +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/IRIS_5_changelog.md +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/Makefile +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.cli.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.hci.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.hcp.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.parse_credentials.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/NGPIris.utils.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/Tutorial.md +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/conf.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/index.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/make.bat +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/docs/modules.rst +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/setup.cfg +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/80MB_test_file +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/a folder of data/80MB_test_file_0 +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/a folder of data/80MB_test_file_1 +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/complete_creds_0.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/complete_creds_1.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/complete_creds_2.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/incomplete_creds_0.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/incomplete_creds_1.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/incomplete_creds_2.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/incomplete_creds_3.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/data/incomplete_creds_4.json +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/test_hci.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/test_hcp.py +0 -0
- {ngpiris-5.3.1 → ngpiris-5.4.1}/tests/test_parse_credentials.py +0 -0
|
@@ -4,11 +4,7 @@ from click.core import Context
|
|
|
4
4
|
from json import dump
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from boto3 import set_stream_logger
|
|
7
|
-
from botocore.paginate import PageIterator, Paginator
|
|
8
7
|
from typing import Any, Generator
|
|
9
|
-
from os import get_terminal_size
|
|
10
|
-
from math import floor
|
|
11
|
-
from tabulate import tabulate
|
|
12
8
|
from bitmath import Byte, TiB
|
|
13
9
|
|
|
14
10
|
from NGPIris.hcp import HCPHandler
|
|
@@ -26,36 +22,9 @@ def _list_objects_generator(hcph : HCPHandler, path : str, name_only : bool, fil
|
|
|
26
22
|
different from `list_objects` in `hcp.py` in order to make the output
|
|
27
23
|
printable in a terminal
|
|
28
24
|
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
max_width = floor(nb_of_cols / 5)
|
|
33
|
-
if (not name_only):
|
|
34
|
-
yield tabulate(
|
|
35
|
-
[],
|
|
36
|
-
headers = ["Key", "LastModified", "ETag", "Size", "StorageClass"],
|
|
37
|
-
tablefmt = "plain",
|
|
38
|
-
stralign = "center"
|
|
39
|
-
) + "\n" + "-"*nb_of_cols + "\n"
|
|
40
|
-
if files_only:
|
|
41
|
-
filter_string = "Contents[?!ends_with(Key, '/')][]" # filter objects that does not end with "/"
|
|
42
|
-
else:
|
|
43
|
-
filter_string = "Contents[*][]"
|
|
44
|
-
for object in pages.search(filter_string):
|
|
45
|
-
if name_only:
|
|
46
|
-
yield str(object["Key"]) + "\n"
|
|
47
|
-
else:
|
|
48
|
-
yield tabulate(
|
|
49
|
-
[
|
|
50
|
-
[str(object["Key"]),
|
|
51
|
-
str(object["LastModified"]),
|
|
52
|
-
str(object["ETag"]),
|
|
53
|
-
str(object["Size"]),
|
|
54
|
-
str(object["StorageClass"])]
|
|
55
|
-
],
|
|
56
|
-
maxcolwidths = max_width,
|
|
57
|
-
tablefmt = "plain"
|
|
58
|
-
) + "\n" + "-"*nb_of_cols + "\n"
|
|
25
|
+
objects = hcph.list_objects(path, name_only, files_only)
|
|
26
|
+
for obj in objects:
|
|
27
|
+
yield str(obj) + "\n"
|
|
59
28
|
|
|
60
29
|
def object_is_folder(object_path : str, hcph : HCPHandler) -> bool:
|
|
61
30
|
return (object_path[-1] == "/") and (hcph.get_object(object_path)["ContentLength"] == 0)
|
|
@@ -457,7 +426,7 @@ def iris_generate_credentials_file(path : str, name : str):
|
|
|
457
426
|
file_path = path + name
|
|
458
427
|
|
|
459
428
|
if not Path(path).is_dir():
|
|
460
|
-
Path(path).mkdir()
|
|
429
|
+
Path(path).mkdir(parents=True)
|
|
461
430
|
else:
|
|
462
431
|
file_path = name
|
|
463
432
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
from NGPIris.parse_credentials import CredentialsHandler
|
|
3
2
|
from NGPIris.hcp.helpers import (
|
|
4
3
|
raise_path_error,
|
|
@@ -41,7 +40,6 @@ from rapidfuzz import (
|
|
|
41
40
|
from requests import get
|
|
42
41
|
from urllib3 import disable_warnings
|
|
43
42
|
from tqdm import tqdm
|
|
44
|
-
|
|
45
43
|
from bitmath import TiB, Byte
|
|
46
44
|
|
|
47
45
|
from typing import Generator
|
|
@@ -256,51 +254,75 @@ class HCPHandler:
|
|
|
256
254
|
response = self.get_response("/namespaces")
|
|
257
255
|
list_of_buckets : list[str] = response["name"]
|
|
258
256
|
return list_of_buckets
|
|
259
|
-
|
|
257
|
+
|
|
260
258
|
@check_mounted
|
|
261
|
-
def list_objects(
|
|
259
|
+
def list_objects(
|
|
260
|
+
self,
|
|
261
|
+
path_key : str = "",
|
|
262
|
+
list_all_bucket_objects : bool = False,
|
|
263
|
+
name_only : bool = False,
|
|
264
|
+
files_only : bool = False
|
|
265
|
+
) -> Generator:
|
|
262
266
|
"""
|
|
263
267
|
List all objects in the mounted bucket as a generator. If one wishes to
|
|
264
268
|
get the result as a list, use :py:function:`list` to type cast the generator
|
|
265
269
|
|
|
266
|
-
:param path_key: Filter string for which keys to list, specifically for finding objects in certain folders.
|
|
270
|
+
:param path_key: Filter string for which keys to list, specifically for finding objects in certain folders. Defaults to \"the root\" of the bucket
|
|
267
271
|
:type path_key: str, optional
|
|
272
|
+
:param list_all_bucket_objects: If True, the value of `path_key` will be ignored and instead will list all objects in the bucket. Defaults to False
|
|
273
|
+
:type list_all_bucket_objects: bool, optional
|
|
268
274
|
:param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
|
|
269
275
|
:type name_only: bool, optional
|
|
270
|
-
:param files_only: If
|
|
276
|
+
:param files_only: If True, only yield file objects. Defaults to False
|
|
271
277
|
:type files_only: bool, optional
|
|
272
|
-
:yield: A generator of all objects in a bucket
|
|
278
|
+
:yield: A generator of all objects in specified folder in a bucket
|
|
273
279
|
:rtype: Generator
|
|
274
280
|
"""
|
|
275
281
|
paginator : Paginator = self.s3_client.get_paginator("list_objects_v2")
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if files_only:
|
|
279
|
-
filter_string = "Contents[?!ends_with(Key, '/')][]"
|
|
282
|
+
if list_all_bucket_objects:
|
|
283
|
+
pages : PageIterator = paginator.paginate(Bucket = self.bucket_name)
|
|
280
284
|
else:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
split_path_key = len(path_key.split("/")) + 1
|
|
285
|
+
pages : PageIterator = paginator.paginate(Bucket = self.bucket_name, Prefix = path_key, Delimiter = "/")
|
|
284
286
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
#
|
|
288
|
-
|
|
289
|
-
if not object:
|
|
287
|
+
for page in pages:
|
|
288
|
+
page : dict | None
|
|
289
|
+
# Check if `page` is None
|
|
290
|
+
if not page:
|
|
290
291
|
break
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
293
|
+
if files_only:
|
|
294
|
+
for file_object in page.get("Contents", []):
|
|
295
|
+
file_object : dict
|
|
296
|
+
if file_object["Key"] != path_key:
|
|
297
|
+
file_object["IsFile"] = True
|
|
298
|
+
if name_only:
|
|
299
|
+
yield file_object["Key"]
|
|
300
|
+
else:
|
|
301
|
+
yield file_object
|
|
302
|
+
else:
|
|
303
|
+
for folder_object in page.get("CommonPrefixes", []):
|
|
304
|
+
folder_object : dict
|
|
305
|
+
folder_object_metadata = self.get_object(folder_object["Prefix"])
|
|
306
|
+
|
|
307
|
+
if name_only:
|
|
308
|
+
yield folder_object["Prefix"]
|
|
309
|
+
else:
|
|
310
|
+
yield {
|
|
311
|
+
"Key" : folder_object["Prefix"],
|
|
312
|
+
"LastModified" : folder_object_metadata["LastModified"],
|
|
313
|
+
"ETag" : folder_object_metadata["ETag"],
|
|
314
|
+
"IsFile" : False,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
for file_object in page.get("Contents", []):
|
|
318
|
+
file_object : dict
|
|
319
|
+
if file_object["Key"] != path_key:
|
|
320
|
+
file_object["IsFile"] = True
|
|
321
|
+
if name_only:
|
|
322
|
+
yield file_object["Key"]
|
|
323
|
+
else:
|
|
324
|
+
yield file_object
|
|
325
|
+
|
|
304
326
|
|
|
305
327
|
@check_mounted
|
|
306
328
|
def get_object(self, key : str) -> dict:
|
|
@@ -427,10 +449,18 @@ class HCPHandler:
|
|
|
427
449
|
raise ObjectDoesNotExist("Could not find object", "\"" + folder_key + "\"", "in bucket", "\"" + str(self.bucket_name) + "\"")
|
|
428
450
|
if Path(local_folder_path).is_dir():
|
|
429
451
|
current_download_size_in_bytes = Byte(0) # For tracking download limit
|
|
430
|
-
|
|
452
|
+
(Path(local_folder_path) / Path(folder_key)).mkdir(parents = True) # Create "base folder"
|
|
453
|
+
for object in self.list_objects(folder_key): # Build the tree with directories or add files:
|
|
431
454
|
p = Path(local_folder_path) / Path(object["Key"])
|
|
432
|
-
if object["
|
|
433
|
-
p.mkdir()
|
|
455
|
+
if not object["IsFile"]: # If the object is a "folder"
|
|
456
|
+
p.mkdir(parents = True)
|
|
457
|
+
self.download_folder(
|
|
458
|
+
folder_key = str(object["Key"]),
|
|
459
|
+
local_folder_path = local_folder_path,
|
|
460
|
+
use_download_limit = use_download_limit,
|
|
461
|
+
show_progress_bar = show_progress_bar,
|
|
462
|
+
download_limit_in_bytes = download_limit_in_bytes - current_download_size_in_bytes
|
|
463
|
+
)
|
|
434
464
|
else: # If the object is a file
|
|
435
465
|
current_download_size_in_bytes += Byte(object["Size"])
|
|
436
466
|
if current_download_size_in_bytes >= download_limit_in_bytes and use_download_limit:
|
|
@@ -573,6 +603,7 @@ class HCPHandler:
|
|
|
573
603
|
key += "/"
|
|
574
604
|
|
|
575
605
|
objects : list[str] = list(self.list_objects(key, name_only = True))
|
|
606
|
+
objects.append(key) # Include the object "folder" path to be deleted
|
|
576
607
|
|
|
577
608
|
if not objects:
|
|
578
609
|
raise RuntimeError("\"" + key + "\"" + " is not a valid path") #TODO: change this error
|
|
@@ -640,11 +671,11 @@ class HCPHandler:
|
|
|
640
671
|
processor = utils.default_process
|
|
641
672
|
|
|
642
673
|
if not name_only:
|
|
643
|
-
full_list = list(self.list_objects())
|
|
674
|
+
full_list = list(self.list_objects(list_all_bucket_objects = True))
|
|
644
675
|
|
|
645
676
|
for item, score, index in process.extract_iter(
|
|
646
677
|
search_string,
|
|
647
|
-
self.list_objects(name_only = True),
|
|
678
|
+
self.list_objects(list_all_bucket_objects = True, name_only = True),
|
|
648
679
|
scorer = fuzz.partial_ratio,
|
|
649
680
|
processor = processor
|
|
650
681
|
):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: NGPIris
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.4.1
|
|
4
4
|
Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
|
|
5
5
|
Description-Content-Type: text/markdown
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,6 +13,7 @@ Requires-Dist: tqdm>=4.66.2
|
|
|
13
13
|
Requires-Dist: click>=8.1.7
|
|
14
14
|
Requires-Dist: bitmath==1.3.3.1
|
|
15
15
|
Requires-Dist: tabulate==0.9.0
|
|
16
|
+
Dynamic: license-file
|
|
16
17
|
|
|
17
18
|
# NGP IRIS 👀
|
|
18
19
|
NGP IRIS, or just Iris, is a tool for interacting with a Hitachi Content Platform (HCP) using S3 in the `boto3` package. NGP Iris is designed with two use cases in mind:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: NGPIris
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.4.1
|
|
4
4
|
Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
|
|
5
5
|
Description-Content-Type: text/markdown
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,6 +13,7 @@ Requires-Dist: tqdm>=4.66.2
|
|
|
13
13
|
Requires-Dist: click>=8.1.7
|
|
14
14
|
Requires-Dist: bitmath==1.3.3.1
|
|
15
15
|
Requires-Dist: tabulate==0.9.0
|
|
16
|
+
Dynamic: license-file
|
|
16
17
|
|
|
17
18
|
# NGP IRIS 👀
|
|
18
19
|
NGP IRIS, or just Iris, is a tool for interacting with a Hitachi Content Platform (HCP) using S3 in the `boto3` package. NGP Iris is designed with two use cases in mind:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|