NGPIris 5.1.1.dev2__tar.gz → 5.2.0__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.1.1.dev2 → ngpiris-5.2.0}/NGPIris/cli/__init__.py +116 -10
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hcp/hcp.py +124 -50
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris.egg-info/PKG-INFO +4 -111
- ngpiris-5.2.0/NGPIris.egg-info/SOURCES.txt +52 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris.egg-info/requires.txt +1 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/PKG-INFO +4 -111
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/README.md +1 -109
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/Tutorial.md +32 -41
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/pyproject.toml +6 -1
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/test_hci.py +5 -1
- ngpiris-5.2.0/tests/test_hcp.py +213 -0
- ngpiris-5.1.1.dev2/NGPIris.egg-info/SOURCES.txt +0 -127
- ngpiris-5.1.1.dev2/docs/_build/.DS_Store +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/IRIS_5_changelog.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.cli.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.hci.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.hcp.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.parse_credentials.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/NGPIris.utils.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/README.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/Tutorial.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/environment.pickle +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/index.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/doctrees/modules.doctree +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/.buildinfo +0 -4
- ngpiris-5.1.1.dev2/docs/_build/html/IRIS_5_changelog.html +0 -462
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.cli.html +0 -137
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.hci.html +0 -280
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.hcp.html +0 -357
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.html +0 -236
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.parse_credentials.html +0 -156
- ngpiris-5.1.1.dev2/docs/_build/html/NGPIris.utils.html +0 -143
- ngpiris-5.1.1.dev2/docs/_build/html/README.html +0 -681
- ngpiris-5.1.1.dev2/docs/_build/html/Tutorial.html +0 -796
- ngpiris-5.1.1.dev2/docs/_build/html/_downloads/ee00c4f6f22dc2671c766f728f00006f/credentials_template.json +0 -14
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/cli.html +0 -338
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hci/exceptions.html +0 -106
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hci/hci.html +0 -250
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hci/helpers.html +0 -204
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hcp/exceptions.html +0 -117
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hcp/hcp.html +0 -655
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hcp/helpers.html +0 -148
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/hcp/statistics.html +0 -137
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/parse_credentials/parse_credentials.html +0 -170
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/NGPIris/utils/utils.html +0 -115
- ngpiris-5.1.1.dev2/docs/_build/html/_modules/index.html +0 -108
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/IRIS_5_changelog.md.txt +0 -43
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.cli.rst.txt +0 -10
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.hci.rst.txt +0 -37
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.hcp.rst.txt +0 -45
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.parse_credentials.rst.txt +0 -21
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.rst.txt +0 -22
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/NGPIris.utils.rst.txt +0 -21
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/README.md.txt +0 -274
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/Tutorial.md.txt +0 -326
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/index.rst.txt +0 -21
- ngpiris-5.1.1.dev2/docs/_build/html/_sources/modules.rst.txt +0 -7
- ngpiris-5.1.1.dev2/docs/_build/html/_static/alabaster.css +0 -708
- ngpiris-5.1.1.dev2/docs/_build/html/_static/basic.css +0 -925
- ngpiris-5.1.1.dev2/docs/_build/html/_static/custom.css +0 -1
- ngpiris-5.1.1.dev2/docs/_build/html/_static/debug.css +0 -69
- ngpiris-5.1.1.dev2/docs/_build/html/_static/doctools.js +0 -156
- ngpiris-5.1.1.dev2/docs/_build/html/_static/documentation_options.js +0 -13
- ngpiris-5.1.1.dev2/docs/_build/html/_static/file.png +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/_static/language_data.js +0 -199
- ngpiris-5.1.1.dev2/docs/_build/html/_static/minus.png +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/_static/plus.png +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/_static/pygments.css +0 -83
- ngpiris-5.1.1.dev2/docs/_build/html/_static/scripts/furo-extensions.js +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/_static/scripts/furo.js +0 -3
- ngpiris-5.1.1.dev2/docs/_build/html/_static/scripts/furo.js.LICENSE.txt +0 -7
- ngpiris-5.1.1.dev2/docs/_build/html/_static/scripts/furo.js.map +0 -1
- ngpiris-5.1.1.dev2/docs/_build/html/_static/searchtools.js +0 -619
- ngpiris-5.1.1.dev2/docs/_build/html/_static/skeleton.css +0 -296
- ngpiris-5.1.1.dev2/docs/_build/html/_static/sphinx_highlight.js +0 -154
- ngpiris-5.1.1.dev2/docs/_build/html/_static/styles/furo-extensions.css +0 -2
- ngpiris-5.1.1.dev2/docs/_build/html/_static/styles/furo-extensions.css.map +0 -1
- ngpiris-5.1.1.dev2/docs/_build/html/_static/styles/furo.css +0 -2
- ngpiris-5.1.1.dev2/docs/_build/html/_static/styles/furo.css.map +0 -1
- ngpiris-5.1.1.dev2/docs/_build/html/genindex.html +0 -476
- ngpiris-5.1.1.dev2/docs/_build/html/index.html +0 -159
- ngpiris-5.1.1.dev2/docs/_build/html/modules.html +0 -148
- ngpiris-5.1.1.dev2/docs/_build/html/objects.inv +0 -0
- ngpiris-5.1.1.dev2/docs/_build/html/py-modindex.html +0 -188
- ngpiris-5.1.1.dev2/docs/_build/html/search.html +0 -120
- ngpiris-5.1.1.dev2/docs/_build/html/searchindex.js +0 -1
- ngpiris-5.1.1.dev2/tests/test_hcp.py +0 -216
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/LICENSE +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/MANIFEST.in +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/__init__.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hci/__init__.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hci/exceptions.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hci/hci.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hci/helpers.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hcp/__init__.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hcp/exceptions.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hcp/helpers.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/hcp/statistics.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/parse_credentials/__init__.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/parse_credentials/parse_credentials.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/utils/__init__.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris/utils/utils.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris.egg-info/dependency_links.txt +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris.egg-info/entry_points.txt +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/NGPIris.egg-info/top_level.txt +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/IRIS_5_changelog.md +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/Makefile +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.cli.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.hci.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.hcp.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.parse_credentials.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/NGPIris.utils.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/conf.py +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/index.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/make.bat +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/docs/modules.rst +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/setup.cfg +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/80MB_test_file +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/a folder of data/80MB_test_file_0 +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/a folder of data/80MB_test_file_1 +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/complete_creds_0.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/complete_creds_1.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/complete_creds_2.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/incomplete_creds_0.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/incomplete_creds_1.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/incomplete_creds_2.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/incomplete_creds_3.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/data/incomplete_creds_4.json +0 -0
- {ngpiris-5.1.1.dev2 → ngpiris-5.2.0}/tests/test_parse_credentials.py +0 -0
|
@@ -19,14 +19,14 @@ def format_list(list_of_things : list) -> str:
|
|
|
19
19
|
list_of_buckets = list(map(lambda s : s + "\n", list_of_things))
|
|
20
20
|
return "".join(list_of_buckets).strip("\n")
|
|
21
21
|
|
|
22
|
-
def _list_objects_generator(hcph : HCPHandler, name_only : bool) -> Generator[str, Any, None]:
|
|
22
|
+
def _list_objects_generator(hcph : HCPHandler, path : str, name_only : bool, files_only : bool) -> Generator[str, Any, None]:
|
|
23
23
|
"""
|
|
24
24
|
Handle object list as a paginator that `click` can handle. It works slightly
|
|
25
25
|
different from `list_objects` in `hcp.py` in order to make the output
|
|
26
26
|
printable in a terminal
|
|
27
27
|
"""
|
|
28
28
|
paginator : Paginator = hcph.s3_client.get_paginator("list_objects_v2")
|
|
29
|
-
pages : PageIterator = paginator.paginate(Bucket = hcph.bucket_name)
|
|
29
|
+
pages : PageIterator = paginator.paginate(Bucket = hcph.bucket_name, Prefix = path)
|
|
30
30
|
(nb_of_cols, _) = get_terminal_size()
|
|
31
31
|
max_width = floor(nb_of_cols / 5)
|
|
32
32
|
if (not name_only):
|
|
@@ -36,7 +36,11 @@ def _list_objects_generator(hcph : HCPHandler, name_only : bool) -> Generator[st
|
|
|
36
36
|
tablefmt = "plain",
|
|
37
37
|
stralign = "center"
|
|
38
38
|
) + "\n" + "-"*nb_of_cols + "\n"
|
|
39
|
-
|
|
39
|
+
if files_only:
|
|
40
|
+
filter_string = "Contents[?!ends_with(Key, '/')][]" # filter objects that does not end with "/"
|
|
41
|
+
else:
|
|
42
|
+
filter_string = "Contents[*][]"
|
|
43
|
+
for object in pages.search(filter_string):
|
|
40
44
|
if name_only:
|
|
41
45
|
yield str(object["Key"]) + "\n"
|
|
42
46
|
else:
|
|
@@ -55,6 +59,11 @@ def _list_objects_generator(hcph : HCPHandler, name_only : bool) -> Generator[st
|
|
|
55
59
|
def object_is_folder(object_path : str, hcph : HCPHandler) -> bool:
|
|
56
60
|
return (object_path[-1] == "/") and (hcph.get_object(object_path)["ContentLength"] == 0)
|
|
57
61
|
|
|
62
|
+
def add_trailing_slash(path : str) -> str:
|
|
63
|
+
if not path[-1] == "/":
|
|
64
|
+
path += "/"
|
|
65
|
+
return path
|
|
66
|
+
|
|
58
67
|
@click.group()
|
|
59
68
|
@click.argument("credentials")
|
|
60
69
|
@click.version_option(package_name = "NGPIris")
|
|
@@ -85,9 +94,13 @@ def upload(context : Context, bucket : str, source : str, destination : str):
|
|
|
85
94
|
"""
|
|
86
95
|
hcph : HCPHandler = get_HCPHandler(context)
|
|
87
96
|
hcph.mount_bucket(bucket)
|
|
97
|
+
destination = add_trailing_slash(destination)
|
|
88
98
|
if Path(source).is_dir():
|
|
99
|
+
source = add_trailing_slash(source)
|
|
89
100
|
hcph.upload_folder(source, destination)
|
|
90
101
|
else:
|
|
102
|
+
file_name = Path(source).name
|
|
103
|
+
destination += file_name
|
|
91
104
|
hcph.upload_file(source, destination)
|
|
92
105
|
|
|
93
106
|
@cli.command()
|
|
@@ -198,22 +211,52 @@ def list_buckets(context : Context):
|
|
|
198
211
|
|
|
199
212
|
@cli.command()
|
|
200
213
|
@click.argument("bucket")
|
|
214
|
+
@click.argument("path", required = False)
|
|
201
215
|
@click.option(
|
|
202
216
|
"-no",
|
|
203
217
|
"--name-only",
|
|
204
218
|
help = "Output only the name of the objects instead of all the associated metadata",
|
|
205
|
-
default = False
|
|
219
|
+
default = False,
|
|
220
|
+
is_flag = True
|
|
221
|
+
)
|
|
222
|
+
@click.option(
|
|
223
|
+
"-p",
|
|
224
|
+
"--pagination",
|
|
225
|
+
help = "Output as a paginator",
|
|
226
|
+
default = False,
|
|
227
|
+
is_flag = True
|
|
228
|
+
)
|
|
229
|
+
@click.option(
|
|
230
|
+
"-fo",
|
|
231
|
+
"--files-only",
|
|
232
|
+
help = "Output only file objects",
|
|
233
|
+
default = False,
|
|
234
|
+
is_flag = True
|
|
206
235
|
)
|
|
207
236
|
@click.pass_context
|
|
208
|
-
def list_objects(context : Context, bucket : str, name_only : bool):
|
|
237
|
+
def list_objects(context : Context, bucket : str, path : str, name_only : bool, pagination : bool, files_only : bool):
|
|
209
238
|
"""
|
|
210
239
|
List the objects in a certain bucket/namespace on the HCP.
|
|
211
240
|
|
|
212
241
|
BUCKET is the name of the bucket in which to list its objects.
|
|
242
|
+
|
|
243
|
+
PATH is an optional argument for where to list the objects
|
|
213
244
|
"""
|
|
214
245
|
hcph : HCPHandler = get_HCPHandler(context)
|
|
215
246
|
hcph.mount_bucket(bucket)
|
|
216
|
-
|
|
247
|
+
if path:
|
|
248
|
+
path_with_slash = add_trailing_slash(path)
|
|
249
|
+
|
|
250
|
+
if not hcph.object_exists(path_with_slash):
|
|
251
|
+
raise RuntimeError("Path does not exist")
|
|
252
|
+
else:
|
|
253
|
+
path_with_slash = ""
|
|
254
|
+
|
|
255
|
+
if pagination:
|
|
256
|
+
click.echo_via_pager(_list_objects_generator(hcph, path_with_slash, name_only, files_only))
|
|
257
|
+
else:
|
|
258
|
+
for obj in hcph.list_objects(path_with_slash, name_only, files_only):
|
|
259
|
+
click.echo(obj)
|
|
217
260
|
|
|
218
261
|
@cli.command()
|
|
219
262
|
@click.argument("bucket")
|
|
@@ -222,23 +265,86 @@ def list_objects(context : Context, bucket : str, name_only : bool):
|
|
|
222
265
|
"-cs",
|
|
223
266
|
"--case_sensitive",
|
|
224
267
|
help = "Use case sensitivity? Default value is False",
|
|
225
|
-
default = False
|
|
268
|
+
default = False,
|
|
269
|
+
is_flag = True
|
|
270
|
+
)
|
|
271
|
+
@click.option(
|
|
272
|
+
"-v",
|
|
273
|
+
"--verbose",
|
|
274
|
+
help = "Get a verbose output of files. Default value is False, since it might be slower",
|
|
275
|
+
default = False,
|
|
276
|
+
is_flag = True
|
|
226
277
|
)
|
|
227
278
|
@click.pass_context
|
|
228
|
-
def simple_search(context : Context, bucket : str, search_string : str, case_sensitive : bool):
|
|
279
|
+
def simple_search(context : Context, bucket : str, search_string : str, case_sensitive : bool, verbose : bool):
|
|
229
280
|
"""
|
|
230
281
|
Make simple search using substrings in a bucket/namespace on the HCP.
|
|
231
282
|
|
|
283
|
+
NOTE: This command does not use the HCI. Instead, it uses a linear search of
|
|
284
|
+
all the objects in the HCP. As such, this search might be slow.
|
|
285
|
+
|
|
286
|
+
BUCKET is the name of the bucket in which to make the search.
|
|
287
|
+
|
|
288
|
+
SEARCH_STRING is any string that is to be used for the search.
|
|
289
|
+
"""
|
|
290
|
+
hcph : HCPHandler = get_HCPHandler(context)
|
|
291
|
+
hcph.mount_bucket(bucket)
|
|
292
|
+
list_of_results = hcph.search_in_bucket(
|
|
293
|
+
search_string,
|
|
294
|
+
name_only = (not verbose),
|
|
295
|
+
case_sensitive = case_sensitive
|
|
296
|
+
)
|
|
297
|
+
click.echo("Search results:")
|
|
298
|
+
for result in list_of_results:
|
|
299
|
+
click.echo(result)
|
|
300
|
+
|
|
301
|
+
@cli.command()
|
|
302
|
+
@click.argument("bucket")
|
|
303
|
+
@click.argument("search_string")
|
|
304
|
+
@click.option(
|
|
305
|
+
"-cs",
|
|
306
|
+
"--case_sensitive",
|
|
307
|
+
help = "Use case sensitivity? Default value is False",
|
|
308
|
+
default = False,
|
|
309
|
+
is_flag = True
|
|
310
|
+
)
|
|
311
|
+
@click.option(
|
|
312
|
+
"-v",
|
|
313
|
+
"--verbose",
|
|
314
|
+
help = "Get a verbose output of files. Default value is False, since it might be slower",
|
|
315
|
+
default = False,
|
|
316
|
+
is_flag = True
|
|
317
|
+
)
|
|
318
|
+
@click.option(
|
|
319
|
+
"-t",
|
|
320
|
+
"--threshold",
|
|
321
|
+
help = "Set the threshold for the fuzzy search score. Default value is 80",
|
|
322
|
+
default = 80
|
|
323
|
+
)
|
|
324
|
+
@click.pass_context
|
|
325
|
+
def fuzzy_search(context : Context, bucket : str, search_string : str, case_sensitive : bool, verbose : bool, threshold : int):
|
|
326
|
+
"""
|
|
327
|
+
Make a fuzzy search using a search string in a bucket/namespace on the HCP.
|
|
328
|
+
|
|
329
|
+
NOTE: This command does not use the HCI. Instead, it uses the RapidFuzz
|
|
330
|
+
library in order to find objects in the HCP. As such, this search might
|
|
331
|
+
be slow.
|
|
332
|
+
|
|
232
333
|
BUCKET is the name of the bucket in which to make the search.
|
|
233
334
|
|
|
234
335
|
SEARCH_STRING is any string that is to be used for the search.
|
|
235
336
|
"""
|
|
236
337
|
hcph : HCPHandler = get_HCPHandler(context)
|
|
237
338
|
hcph.mount_bucket(bucket)
|
|
238
|
-
list_of_results = hcph.
|
|
339
|
+
list_of_results = hcph.fuzzy_search_in_bucket(
|
|
340
|
+
search_string,
|
|
341
|
+
name_only = (not verbose),
|
|
342
|
+
case_sensitive = case_sensitive,
|
|
343
|
+
threshold = threshold
|
|
344
|
+
)
|
|
239
345
|
click.echo("Search results:")
|
|
240
346
|
for result in list_of_results:
|
|
241
|
-
click.echo(
|
|
347
|
+
click.echo(result)
|
|
242
348
|
|
|
243
349
|
@cli.command()
|
|
244
350
|
@click.argument("bucket")
|
|
@@ -31,9 +31,13 @@ from os import (
|
|
|
31
31
|
from json import dumps
|
|
32
32
|
from parse import (
|
|
33
33
|
parse,
|
|
34
|
-
search,
|
|
35
34
|
Result
|
|
36
35
|
)
|
|
36
|
+
from rapidfuzz import (
|
|
37
|
+
fuzz,
|
|
38
|
+
process,
|
|
39
|
+
utils
|
|
40
|
+
)
|
|
37
41
|
from requests import get
|
|
38
42
|
from urllib3 import disable_warnings
|
|
39
43
|
from tqdm import tqdm
|
|
@@ -173,7 +177,9 @@ class HCPHandler:
|
|
|
173
177
|
elif bucket_name:
|
|
174
178
|
pass
|
|
175
179
|
else:
|
|
176
|
-
raise RuntimeError("No bucket selected. Either use `mount_bucket` first or supply the optional `bucket_name`
|
|
180
|
+
raise RuntimeError("No bucket selected. Either use `mount_bucket` first or supply the optional `bucket_name` parameter for `test_connection`")
|
|
181
|
+
|
|
182
|
+
response = {}
|
|
177
183
|
try:
|
|
178
184
|
response = dict(self.s3_client.head_bucket(Bucket = bucket_name))
|
|
179
185
|
except EndpointConnectionError as e: # pragma: no cover
|
|
@@ -230,7 +236,7 @@ class HCPHandler:
|
|
|
230
236
|
return list_of_buckets
|
|
231
237
|
|
|
232
238
|
@check_mounted
|
|
233
|
-
def list_objects(self, path_key : str = "", name_only : bool = False) -> Generator:
|
|
239
|
+
def list_objects(self, path_key : str = "", name_only : bool = False, files_only : bool = False) -> Generator:
|
|
234
240
|
"""
|
|
235
241
|
List all objects in the mounted bucket as a generator. If one wishes to
|
|
236
242
|
get the result as a list, use :py:function:`list` to type cast the generator
|
|
@@ -239,16 +245,35 @@ class HCPHandler:
|
|
|
239
245
|
:type path_key: str, optional
|
|
240
246
|
:param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
|
|
241
247
|
:type name_only: bool, optional
|
|
248
|
+
:param files_only: If true, only yield file objects. Defaults to False
|
|
249
|
+
:type files_only: bool, optional
|
|
242
250
|
:yield: A generator of all objects in a bucket
|
|
243
251
|
:rtype: Generator
|
|
244
252
|
"""
|
|
245
253
|
paginator : Paginator = self.s3_client.get_paginator("list_objects_v2")
|
|
246
|
-
pages : PageIterator = paginator.paginate(Bucket = self.bucket_name)
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
pages : PageIterator = paginator.paginate(Bucket = self.bucket_name, Prefix = path_key)
|
|
255
|
+
|
|
256
|
+
if files_only:
|
|
257
|
+
filter_string = "Contents[?!ends_with(Key, '/')][]"
|
|
258
|
+
else:
|
|
259
|
+
filter_string = "Contents[*][]"
|
|
260
|
+
|
|
261
|
+
split_path_key = len(path_key.split("/")) + 1
|
|
262
|
+
|
|
263
|
+
pages_filtered = pages.search(filter_string)
|
|
264
|
+
for object in pages_filtered:
|
|
265
|
+
# Split the object key by "/"
|
|
266
|
+
split_object = object["Key"].split("/")
|
|
267
|
+
# Check if the object is within the specified path_key depth
|
|
268
|
+
if len(split_object) <= split_path_key:
|
|
269
|
+
# Skip objects that are not at the desired depth
|
|
270
|
+
if (len(split_object) == split_path_key) and split_object[-1]:
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
if name_only:
|
|
274
|
+
yield str(object["Key"])
|
|
275
|
+
else:
|
|
276
|
+
yield object
|
|
252
277
|
|
|
253
278
|
@check_mounted
|
|
254
279
|
def get_object(self, key : str) -> dict:
|
|
@@ -378,6 +403,9 @@ class HCPHandler:
|
|
|
378
403
|
file_name = Path(local_file_path).name
|
|
379
404
|
key = file_name
|
|
380
405
|
|
|
406
|
+
if "\\" in local_file_path:
|
|
407
|
+
raise RuntimeError("The \"\\\" character is not allowed in the file path")
|
|
408
|
+
|
|
381
409
|
if self.object_exists(key):
|
|
382
410
|
raise ObjectAlreadyExist("The object \"" + key + "\" already exist in the mounted bucket")
|
|
383
411
|
else:
|
|
@@ -426,25 +454,24 @@ class HCPHandler:
|
|
|
426
454
|
:type verbose: bool, optional
|
|
427
455
|
"""
|
|
428
456
|
object_list = []
|
|
457
|
+
does_not_exist = []
|
|
429
458
|
for key in keys:
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
459
|
+
if self.object_exists(key):
|
|
460
|
+
object_list.append({"Key" : key})
|
|
461
|
+
else:
|
|
462
|
+
does_not_exist.append(key)
|
|
433
463
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
does_not_exist.append("- " + key + "\n")
|
|
446
|
-
if does_not_exist:
|
|
447
|
-
print("The following could not be deleted because they didn't exist: \n" + "".join(does_not_exist))
|
|
464
|
+
if object_list:
|
|
465
|
+
deletion_dict = {"Objects": object_list}
|
|
466
|
+
response : dict = self.s3_client.delete_objects(
|
|
467
|
+
Bucket = self.bucket_name,
|
|
468
|
+
Delete = deletion_dict
|
|
469
|
+
)
|
|
470
|
+
if verbose:
|
|
471
|
+
print(dumps(response, indent=4))
|
|
472
|
+
|
|
473
|
+
if verbose and does_not_exist:
|
|
474
|
+
print("The following could not be deleted because they didn't exist: \n" + "\n".join(does_not_exist))
|
|
448
475
|
|
|
449
476
|
@check_mounted
|
|
450
477
|
def delete_object(self, key : str, verbose : bool = True) -> None:
|
|
@@ -461,51 +488,98 @@ class HCPHandler:
|
|
|
461
488
|
@check_mounted
|
|
462
489
|
def delete_folder(self, key : str, verbose : bool = True) -> None:
|
|
463
490
|
"""
|
|
464
|
-
Delete a folder of objects in the mounted bucket. If there are subfolders, a RuntimeError is
|
|
491
|
+
Delete a folder of objects in the mounted bucket. If there are subfolders, a RuntimeError is raised
|
|
465
492
|
|
|
466
493
|
:param key: The folder of objects to be deleted
|
|
467
494
|
:type key: str
|
|
468
495
|
:param verbose: Print the result of the deletion. defaults to True
|
|
469
496
|
:type verbose: bool, optional
|
|
470
|
-
:raises RuntimeError: If there are subfolders, a RuntimeError is
|
|
497
|
+
:raises RuntimeError: If there are subfolders, a RuntimeError is raised
|
|
471
498
|
"""
|
|
472
499
|
if key[-1] != "/":
|
|
473
500
|
key += "/"
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
for object_path in
|
|
481
|
-
if object_path[-1] == "/":
|
|
501
|
+
|
|
502
|
+
objects : list[str] = list(self.list_objects(key, name_only = True))
|
|
503
|
+
|
|
504
|
+
if not objects:
|
|
505
|
+
raise RuntimeError("\"" + key + "\"" + " is not a valid path") #TODO: change this error
|
|
506
|
+
|
|
507
|
+
for object_path in objects:
|
|
508
|
+
if (object_path[-1] == "/") and (not object_path == key): # `objects` might contain key, in which case everything is fine
|
|
482
509
|
raise RuntimeError("There are subfolders in this folder. Please remove these first, before deleting this one")
|
|
483
|
-
self.delete_objects(
|
|
510
|
+
self.delete_objects(objects, verbose = verbose)
|
|
484
511
|
|
|
485
512
|
@check_mounted
|
|
486
|
-
def
|
|
513
|
+
def search_in_bucket(
|
|
514
|
+
self,
|
|
515
|
+
search_string : str,
|
|
516
|
+
name_only : bool = True,
|
|
517
|
+
case_sensitive : bool = False
|
|
518
|
+
) -> Generator:
|
|
487
519
|
"""
|
|
488
|
-
Simple search method using substrings in order to find certain
|
|
520
|
+
Simple search method using exact substrings in order to find certain
|
|
521
|
+
objects. Case insensitive by default. Does not utilise the HCI
|
|
489
522
|
|
|
490
523
|
:param search_string: Substring to be used in the search
|
|
491
524
|
:type search_string: str
|
|
492
525
|
|
|
526
|
+
:param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
|
|
527
|
+
:type name_only: bool, optional
|
|
528
|
+
|
|
493
529
|
:param case_sensitive: Case sensitivity. Defaults to False
|
|
494
530
|
:type case_sensitive: bool, optional
|
|
495
531
|
|
|
496
|
-
:return:
|
|
497
|
-
:rtype:
|
|
532
|
+
:return: A generator of objects based on the search string
|
|
533
|
+
:rtype: Generator
|
|
534
|
+
"""
|
|
535
|
+
return self.fuzzy_search_in_bucket(search_string, name_only, case_sensitive, 100)
|
|
536
|
+
|
|
537
|
+
@check_mounted
|
|
538
|
+
def fuzzy_search_in_bucket(
|
|
539
|
+
self,
|
|
540
|
+
search_string : str,
|
|
541
|
+
name_only : bool = True,
|
|
542
|
+
case_sensitive : bool = False,
|
|
543
|
+
threshold : int = 80
|
|
544
|
+
) -> Generator:
|
|
545
|
+
"""
|
|
546
|
+
Fuzzy search implementation based on the `RapidFuzz` library.
|
|
547
|
+
|
|
548
|
+
:param search_string: Substring to be used in the search
|
|
549
|
+
:type search_string: str
|
|
550
|
+
|
|
551
|
+
:param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
|
|
552
|
+
:type name_only: bool, optional
|
|
553
|
+
|
|
554
|
+
:param case_sensitive: Case sensitivity. Defaults to False
|
|
555
|
+
:type case_sensitive: bool, optional
|
|
556
|
+
|
|
557
|
+
:param threshold: The fuzzy search similarity score. Defaults to 80
|
|
558
|
+
:type threshold: int, optional
|
|
559
|
+
|
|
560
|
+
:return: A generator of objects based on the search string
|
|
561
|
+
:rtype: Generator
|
|
498
562
|
"""
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
563
|
+
|
|
564
|
+
if case_sensitive:
|
|
565
|
+
processor = None
|
|
566
|
+
else:
|
|
567
|
+
processor = utils.default_process
|
|
568
|
+
|
|
569
|
+
if not name_only:
|
|
570
|
+
full_list = list(self.list_objects())
|
|
571
|
+
|
|
572
|
+
for item, score, index in process.extract_iter(
|
|
502
573
|
search_string,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
574
|
+
self.list_objects(name_only = True),
|
|
575
|
+
scorer = fuzz.partial_ratio,
|
|
576
|
+
processor = processor
|
|
577
|
+
):
|
|
578
|
+
if score >= threshold:
|
|
579
|
+
if name_only:
|
|
580
|
+
yield item
|
|
581
|
+
else:
|
|
582
|
+
yield full_list[index] # type: ignore
|
|
509
583
|
|
|
510
584
|
@check_mounted
|
|
511
585
|
def get_object_acl(self, key : str) -> dict:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: NGPIris
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0
|
|
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
|
|
@@ -8,6 +8,7 @@ Requires-Dist: requests>=2.31.0
|
|
|
8
8
|
Requires-Dist: urllib3==1.26.19
|
|
9
9
|
Requires-Dist: boto3>=1.26.76
|
|
10
10
|
Requires-Dist: parse>=1.19.1
|
|
11
|
+
Requires-Dist: RapidFuzz>=3.10.1
|
|
11
12
|
Requires-Dist: tqdm>=4.66.2
|
|
12
13
|
Requires-Dist: click>=8.1.7
|
|
13
14
|
Requires-Dist: bitmath==1.3.3.1
|
|
@@ -164,116 +165,8 @@ Commands:
|
|
|
164
165
|
test-connection Test the connection to a bucket/namespace.
|
|
165
166
|
upload Upload files to an HCP bucket/namespace.
|
|
166
167
|
```
|
|
168
|
+
Each sub-command has its own help message and is displayed by `iris path/to/your/credentials.json sub-command --help`
|
|
167
169
|
|
|
168
|
-
#### The `delete-folder` command
|
|
169
|
-
```
|
|
170
|
-
Usage: iris CREDENTIALS delete-folder [OPTIONS] FOLDER BUCKET
|
|
171
|
-
|
|
172
|
-
Delete a folder from an HCP bucket/namespace.
|
|
173
|
-
|
|
174
|
-
FOLDER is the name of the folder to be deleted.
|
|
175
|
-
|
|
176
|
-
BUCKET is the name of the bucket where the folder to be deleted exist.
|
|
177
|
-
|
|
178
|
-
Options:
|
|
179
|
-
--help Show this message and exit.
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
#### The `delete-object` command
|
|
183
|
-
```
|
|
184
|
-
Usage: iris CREDENTIALS delete-object [OPTIONS] OBJECT BUCKET
|
|
185
|
-
|
|
186
|
-
Delete an object from an HCP bucket/namespace.
|
|
187
|
-
|
|
188
|
-
OBJECT is the name of the object to be deleted.
|
|
189
|
-
|
|
190
|
-
BUCKET is the name of the bucket where the object to be deleted exist.
|
|
191
|
-
|
|
192
|
-
Options:
|
|
193
|
-
--help Show this message and exit.
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
#### The `download` command
|
|
197
|
-
```
|
|
198
|
-
Usage: iris CREDENTIALS download [OPTIONS] OBJECT BUCKET LOCAL_PATH
|
|
199
|
-
|
|
200
|
-
Download files from an HCP bucket/namespace.
|
|
201
|
-
|
|
202
|
-
OBJECT is the name of the object to be downloaded.
|
|
203
|
-
|
|
204
|
-
BUCKET is the name of the upload destination bucket.
|
|
205
|
-
|
|
206
|
-
LOCAL_PATH is the path to where the downloaded objects are to be stored
|
|
207
|
-
locally.
|
|
208
|
-
|
|
209
|
-
Options:
|
|
210
|
-
--help Show this message and exit.
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
#### The `list-buckets` command
|
|
214
|
-
```
|
|
215
|
-
Usage: iris CREDENTIALS list-buckets [OPTIONS]
|
|
216
|
-
|
|
217
|
-
List the available buckets/namespaces on the HCP.
|
|
218
|
-
|
|
219
|
-
Options:
|
|
220
|
-
--help Show this message and exit.
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
#### The `list-objects` command
|
|
224
|
-
```
|
|
225
|
-
Usage: iris CREDENTIALS list-objects [OPTIONS] BUCKET
|
|
226
|
-
|
|
227
|
-
List the objects in a certain bucket/namespace on the HCP.
|
|
228
|
-
|
|
229
|
-
BUCKET is the name of the bucket in which to list its objects.
|
|
230
|
-
|
|
231
|
-
Options:
|
|
232
|
-
-no, --name-only BOOLEAN Output only the name of the objects instead of all
|
|
233
|
-
the associated metadata
|
|
234
|
-
--help Show this message and exit.
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
#### The `simple-search` command
|
|
238
|
-
```
|
|
239
|
-
Usage: iris CREDENTIALS simple-search [OPTIONS] BUCKET SEARCH_STRING
|
|
240
|
-
|
|
241
|
-
Make simple search using substrings in a bucket/namespace on the HCP.
|
|
242
|
-
|
|
243
|
-
BUCKET is the name of the bucket in which to make the search.
|
|
244
|
-
|
|
245
|
-
SEARCH_STRING is any string that is to be used for the search.
|
|
246
|
-
|
|
247
|
-
Options:
|
|
248
|
-
-cs, --case_sensitive BOOLEAN Use case sensitivity?
|
|
249
|
-
--help Show this message and exit.
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
#### The `test-connection` command
|
|
253
|
-
```
|
|
254
|
-
Usage: iris CREDENTIALS test-connection [OPTIONS] BUCKET
|
|
255
|
-
|
|
256
|
-
Test the connection to a bucket/namespace.
|
|
257
|
-
|
|
258
|
-
BUCKET is the name of the bucket for which a connection test should be made.
|
|
259
|
-
|
|
260
|
-
Options:
|
|
261
|
-
--help Show this message and exit.
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
#### The `upload` command
|
|
265
|
-
```
|
|
266
|
-
Usage: iris CREDENTIALS upload [OPTIONS] FILE_OR_FOLDER BUCKET
|
|
267
|
-
|
|
268
|
-
Upload files to an HCP bucket/namespace.
|
|
269
|
-
|
|
270
|
-
FILE-OR-FOLDER is the path to the file or folder of files to be uploaded.
|
|
271
|
-
|
|
272
|
-
BUCKET is the name of the upload destination bucket.
|
|
273
|
-
|
|
274
|
-
Options:
|
|
275
|
-
--help Show this message and exit.
|
|
276
|
-
```
|
|
277
170
|
## Testing
|
|
278
171
|
Assuming that the repository has been cloned, run the following tests:
|
|
279
172
|
```shell
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
NGPIris/__init__.py
|
|
6
|
+
NGPIris.egg-info/PKG-INFO
|
|
7
|
+
NGPIris.egg-info/SOURCES.txt
|
|
8
|
+
NGPIris.egg-info/dependency_links.txt
|
|
9
|
+
NGPIris.egg-info/entry_points.txt
|
|
10
|
+
NGPIris.egg-info/requires.txt
|
|
11
|
+
NGPIris.egg-info/top_level.txt
|
|
12
|
+
NGPIris/cli/__init__.py
|
|
13
|
+
NGPIris/hci/__init__.py
|
|
14
|
+
NGPIris/hci/exceptions.py
|
|
15
|
+
NGPIris/hci/hci.py
|
|
16
|
+
NGPIris/hci/helpers.py
|
|
17
|
+
NGPIris/hcp/__init__.py
|
|
18
|
+
NGPIris/hcp/exceptions.py
|
|
19
|
+
NGPIris/hcp/hcp.py
|
|
20
|
+
NGPIris/hcp/helpers.py
|
|
21
|
+
NGPIris/hcp/statistics.py
|
|
22
|
+
NGPIris/parse_credentials/__init__.py
|
|
23
|
+
NGPIris/parse_credentials/parse_credentials.py
|
|
24
|
+
NGPIris/utils/__init__.py
|
|
25
|
+
NGPIris/utils/utils.py
|
|
26
|
+
docs/IRIS_5_changelog.md
|
|
27
|
+
docs/Makefile
|
|
28
|
+
docs/NGPIris.cli.rst
|
|
29
|
+
docs/NGPIris.hci.rst
|
|
30
|
+
docs/NGPIris.hcp.rst
|
|
31
|
+
docs/NGPIris.parse_credentials.rst
|
|
32
|
+
docs/NGPIris.rst
|
|
33
|
+
docs/NGPIris.utils.rst
|
|
34
|
+
docs/Tutorial.md
|
|
35
|
+
docs/conf.py
|
|
36
|
+
docs/index.rst
|
|
37
|
+
docs/make.bat
|
|
38
|
+
docs/modules.rst
|
|
39
|
+
tests/test_hci.py
|
|
40
|
+
tests/test_hcp.py
|
|
41
|
+
tests/test_parse_credentials.py
|
|
42
|
+
tests/data/80MB_test_file
|
|
43
|
+
tests/data/complete_creds_0.json
|
|
44
|
+
tests/data/complete_creds_1.json
|
|
45
|
+
tests/data/complete_creds_2.json
|
|
46
|
+
tests/data/incomplete_creds_0.json
|
|
47
|
+
tests/data/incomplete_creds_1.json
|
|
48
|
+
tests/data/incomplete_creds_2.json
|
|
49
|
+
tests/data/incomplete_creds_3.json
|
|
50
|
+
tests/data/incomplete_creds_4.json
|
|
51
|
+
tests/data/a folder of data/80MB_test_file_0
|
|
52
|
+
tests/data/a folder of data/80MB_test_file_1
|