cmem-cmemc 23.2__py3-none-any.whl → 23.3.0__py3-none-any.whl

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.
@@ -4,9 +4,9 @@ import os
4
4
  from collections import Counter
5
5
  from contextlib import suppress
6
6
 
7
- import requests
8
- from bs4 import BeautifulSoup
9
7
  from click import Context
8
+ from click.parser import split_arg_string
9
+ from click.shell_completion import CompletionItem
10
10
  from natsort import natsorted, ns
11
11
  from prometheus_client.parser import text_string_to_metric_families
12
12
 
@@ -14,7 +14,7 @@ from cmem.cmemc.cli.context import CONTEXT
14
14
  from cmem.cmemc.cli.utils import (
15
15
  get_graphs,
16
16
  metric_get_labels,
17
- metrics_get_dict, struct_to_table
17
+ metrics_get_dict, struct_to_table, get_published_packages
18
18
  )
19
19
  from cmem.cmempy.dp.admin import get_prometheus_data
20
20
  from cmem.cmempy.health import get_complete_status_info
@@ -31,9 +31,12 @@ from cmem.cmempy.workspace import (
31
31
  )
32
32
  from cmem.cmempy.workspace.projects.project import get_projects
33
33
  from cmem.cmempy.workspace.projects.resources import get_all_resources
34
+ from cmem.cmempy.workspace.projects.variables import get_all_variables
34
35
  from cmem.cmempy.workspace.python import list_packages
35
36
  from cmem.cmempy.workspace.search import list_items
36
37
 
38
+ from cmem.cmempy.workspace.projects.datasets.dataset import get_dataset
39
+
37
40
  SORT_BY_KEY = 0
38
41
  SORT_BY_DESC = 1
39
42
 
@@ -69,7 +72,6 @@ def _finalize_completion(
69
72
  return candidates
70
73
  # remove duplicates
71
74
  candidates = list(set(candidates))
72
-
73
75
  if isinstance(candidates[0], str):
74
76
  # list of strings filtering and sorting
75
77
  filtered_candidates = [
@@ -97,22 +99,56 @@ def _finalize_completion(
97
99
  or str(element[1]).lower().find(incomplete) != -1
98
100
  ]
99
101
  if nat_sort:
100
- return natsorted(
102
+ sorted_list = natsorted(
101
103
  seq=filtered_candidates,
102
104
  key=lambda k: k[sort_by], # type: ignore
103
105
  alg=ns.IGNORECASE,
104
106
  reverse=reverse
105
107
  )
106
- return sorted(
107
- filtered_candidates,
108
- key=lambda x: (str(x[sort_by]).casefold(), str(x[sort_by])),
109
- reverse=reverse
110
- )
108
+ else:
109
+ sorted_list = sorted(
110
+ filtered_candidates,
111
+ key=lambda x: (str(x[sort_by]).casefold(), str(x[sort_by])),
112
+ reverse=reverse
113
+ )
114
+ return [
115
+ CompletionItem(
116
+ value=element[0].replace(":", r"\:"),
117
+ help=element[1]
118
+ ) for element in sorted_list
119
+ ]
111
120
  raise ValueError(
112
121
  "candidates should be a list of strings or a list of tuples."
113
122
  )
114
123
 
115
124
 
125
+ def _get_completion_args(incomplete):
126
+ """get completion args
127
+
128
+ This is a workaround to get partial tuple options in a completion function
129
+ see https://github.com/pallets/click/issues/2597
130
+ """
131
+ args = split_arg_string(os.environ["COMP_WORDS"])
132
+ if incomplete and len(args) > 0 and args[len(args)-1] == incomplete:
133
+ args.pop()
134
+ return args
135
+
136
+
137
+ def _ignore_option(option, params):
138
+ """
139
+ Check if the given 'option' is present in the 'params' dictionary
140
+ or any of its values.
141
+ """
142
+ ignore_project_id = False
143
+ for _ in params:
144
+ if hasattr(params[_], '__iter__') and option in params[_]:
145
+ ignore_project_id = True
146
+ elif option == params[_]:
147
+ ignore_project_id = True
148
+
149
+ return ignore_project_id
150
+
151
+
116
152
  def add_metadata_parameter(list_=None):
117
153
  """Extend a list with metadata keys and key descriptions."""
118
154
  if list_ is None:
@@ -156,19 +192,28 @@ def add_read_only_and_uri_property_parameters(list_=None):
156
192
  return list_
157
193
 
158
194
 
159
- def dataset_parameter(ctx, args, incomplete):
195
+ def dataset_parameter(ctx, param, incomplete):
160
196
  """Prepare a list of dataset parameters for a dataset type."""
161
- CONTEXT.set_connection_from_args(args)
197
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
198
+ args = _get_completion_args(incomplete)
162
199
  incomplete = incomplete.lower()
163
200
  # look if cursor is in value position of the -p option and
164
201
  # return nothing in case it is (values are not completed atm)
165
202
  if args[len(args) - 2] in ("-p", "--parameter"):
166
203
  return []
167
204
  # try to determine the dataset type
168
- dataset_type = None
169
- for num, arg in enumerate(args):
170
- if arg == "--type":
171
- dataset_type = args[num + 1]
205
+ dataset_type = ctx.params.get('dataset_type')
206
+ if dataset_type is None:
207
+ try:
208
+ dataset_id = ctx.args[0]
209
+ project = get_dataset(
210
+ project_name=dataset_id.split(":")[0],
211
+ dataset_name=dataset_id.split(":")[1]
212
+ )
213
+ dataset_type = project["data"]["type"]
214
+ except IndexError:
215
+ pass
216
+
172
217
  # without type, we know nothing
173
218
  if dataset_type is None:
174
219
  return []
@@ -192,12 +237,13 @@ def dataset_parameter(ctx, args, incomplete):
192
237
  or key[1].lower().find(incomplete.lower()) != -1
193
238
  )
194
239
  ]
195
- return options
196
240
 
241
+ return [CompletionItem(value=option[0], help=option[1]) for option in options]
197
242
 
198
- def dataset_types(ctx, args, incomplete):
243
+
244
+ def dataset_types(ctx, param, incomplete):
199
245
  """Prepare a list of dataset types."""
200
- CONTEXT.set_connection_from_args(args)
246
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
201
247
  incomplete = incomplete.lower()
202
248
  options = []
203
249
  plugins = get_task_plugins()
@@ -215,31 +261,33 @@ def dataset_types(ctx, args, incomplete):
215
261
  option
216
262
  )
217
263
  )
218
- options = sorted(options, key=lambda k: k[1].lower())
219
- return options
264
+ return _finalize_completion(
265
+ candidates=options,
266
+ incomplete=incomplete,
267
+ sort_by=SORT_BY_DESC
268
+ )
220
269
 
221
270
 
222
- def dataset_ids(ctx, args, incomplete):
271
+ def dataset_ids(ctx, param, incomplete):
223
272
  """Prepare a list of projectid:datasetid dataset identifier."""
224
- CONTEXT.set_connection_from_args(args)
273
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
225
274
  options = []
226
275
  results = list_items(item_type="dataset")
227
276
  datasets = results["results"]
228
277
  for _ in datasets:
229
278
  options.append(
230
279
  (
231
- _["projectId"] + r"\:" + _["id"],
280
+ _['projectId'] + ":" + _['id'],
232
281
  _["label"]
233
282
  )
234
283
  )
235
284
  return _finalize_completion(
236
285
  candidates=options,
237
- incomplete=incomplete,
238
- sort_by=SORT_BY_DESC
286
+ incomplete=incomplete
239
287
  )
240
288
 
241
289
 
242
- def dataset_list_filter(ctx, args, incomplete):
290
+ def dataset_list_filter(ctx, param, incomplete):
243
291
  """Prepare a list of filter names and values for dataset list filter."""
244
292
  filter_names = [
245
293
  (
@@ -270,17 +318,18 @@ def dataset_list_filter(ctx, args, incomplete):
270
318
  )
271
319
  ]
272
320
  options = []
321
+ args = _get_completion_args(incomplete)
273
322
  if args[len(args) - 1] == "--filter":
274
323
  options = _finalize_completion(
275
324
  candidates=filter_names,
276
325
  incomplete=incomplete
277
326
  )
278
327
  if args[len(args) - 1] == "type":
279
- options = dataset_types(ctx, args, incomplete)
328
+ options = dataset_types(ctx, param, incomplete)
280
329
  if args[len(args) - 1] == "project":
281
- options = project_ids(ctx, args, incomplete)
330
+ options = project_ids(ctx, param, incomplete)
282
331
  if args[len(args) - 1] == "tag":
283
- options = tag_labels(ctx, args, incomplete, "dataset")
332
+ options = tag_labels(ctx, param, incomplete, "dataset")
284
333
  if args[len(args) - 1] == "regex":
285
334
  options = _finalize_completion(
286
335
  candidates=filter_regex,
@@ -289,9 +338,9 @@ def dataset_list_filter(ctx, args, incomplete):
289
338
  return options
290
339
 
291
340
 
292
- def resource_ids(ctx, args, incomplete):
341
+ def resource_ids(ctx, param, incomplete):
293
342
  """Prepare a list of projectid:resourceid resource identifier."""
294
- CONTEXT.set_connection_from_args(args)
343
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
295
344
  options = []
296
345
  for _ in get_all_resources():
297
346
  options.append(
@@ -306,9 +355,9 @@ def resource_ids(ctx, args, incomplete):
306
355
  )
307
356
 
308
357
 
309
- def scheduler_ids(ctx, args, incomplete):
358
+ def scheduler_ids(ctx, param, incomplete):
310
359
  """Prepare a list of projectid:schedulerid scheduler identifier."""
311
- CONTEXT.set_connection_from_args(args)
360
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
312
361
  options = []
313
362
  schedulers = list_items(
314
363
  item_type="task",
@@ -319,11 +368,11 @@ def scheduler_ids(ctx, args, incomplete):
319
368
  }]
320
369
  )["results"]
321
370
  for _ in schedulers:
322
- if _["projectId"] + ":" + _["id"] in args:
371
+ if _ignore_option(_["projectId"] + ":" + _["id"], ctx.params):
323
372
  continue
324
373
  options.append(
325
374
  (
326
- _["projectId"] + r"\:" + _["id"],
375
+ _["projectId"] + ":" + _["id"],
327
376
  _["label"]
328
377
  )
329
378
  )
@@ -334,9 +383,9 @@ def scheduler_ids(ctx, args, incomplete):
334
383
  )
335
384
 
336
385
 
337
- def metric_ids(ctx, args, incomplete):
386
+ def metric_ids(ctx, param, incomplete):
338
387
  """Prepare a list of metric identifier."""
339
- CONTEXT.set_connection_from_args(args)
388
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
340
389
  options = []
341
390
 
342
391
  data = get_prometheus_data().text
@@ -354,13 +403,13 @@ def metric_ids(ctx, args, incomplete):
354
403
  )
355
404
 
356
405
 
357
- def metric_label_filter(ctx, args, incomplete):
406
+ def metric_label_filter(ctx, param, incomplete):
358
407
  """Prepare a list of label name or values."""
359
- # TODO: this completion does not take care of the --job option
360
- CONTEXT.set_connection_from_args(args)
408
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
409
+ args = _get_completion_args(incomplete)
361
410
  incomplete = incomplete.lower()
362
411
  options = []
363
- metric_id = args[3]
412
+ metric_id = ctx.args[0]
364
413
  labels = metric_get_labels(metrics_get_dict()[metric_id])
365
414
  if args[len(args) - 1] in "--filter":
366
415
  # we are in the name position
@@ -376,9 +425,9 @@ def metric_label_filter(ctx, args, incomplete):
376
425
  )
377
426
 
378
427
 
379
- def vocabularies(ctx, args, incomplete, filter_="all"):
428
+ def vocabularies(ctx, param, incomplete, filter_="all"):
380
429
  """Prepare a list of vocabulary graphs for auto-completion."""
381
- CONTEXT.set_connection_from_args(args)
430
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
382
431
  options = []
383
432
  try:
384
433
  vocabs = get_vocabularies(filter_=filter_)
@@ -387,9 +436,9 @@ def vocabularies(ctx, args, incomplete, filter_="all"):
387
436
  return []
388
437
  for _ in vocabs:
389
438
  url = _["iri"]
390
- if url in args:
439
+ if _ignore_option(url, ctx.params):
391
440
  continue
392
- url = _["iri"].replace(":", r"\:")
441
+ url = _["iri"]
393
442
  try:
394
443
  label = _["label"]["title"]
395
444
  except (KeyError, TypeError):
@@ -402,14 +451,14 @@ def vocabularies(ctx, args, incomplete, filter_="all"):
402
451
  )
403
452
 
404
453
 
405
- def installed_vocabularies(ctx, args, incomplete):
454
+ def installed_vocabularies(ctx, param, incomplete):
406
455
  """Prepare a list of installed vocabulary graphs."""
407
- return vocabularies(ctx, args, incomplete, filter_="installed")
456
+ return vocabularies(ctx, param, incomplete, filter_="installed")
408
457
 
409
458
 
410
- def installable_vocabularies(ctx, args, incomplete):
459
+ def installable_vocabularies(ctx, param, incomplete):
411
460
  """Prepare a list of installable vocabulary graphs."""
412
- return vocabularies(ctx, args, incomplete, filter_="installable")
461
+ return vocabularies(ctx, param, incomplete, filter_="installable")
413
462
 
414
463
 
415
464
  def file_list(incomplete="", suffix="", description="", prefix=""):
@@ -428,12 +477,12 @@ def file_list(incomplete="", suffix="", description="", prefix=""):
428
477
  )
429
478
 
430
479
 
431
- def workflow_io_ids(ctx, args, incomplete):
480
+ def workflow_io_ids(ctx, param, incomplete):
432
481
  """Prepare a list of io workflows."""
433
- CONTEXT.set_connection_from_args(args)
482
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
434
483
  options = []
435
484
  for _ in get_workflows_io():
436
- workflow_id = _["projectId"] + r"\:" + _["id"]
485
+ workflow_id = _["projectId"] + ":" + _["id"]
437
486
  label = _["label"]
438
487
  options.append((workflow_id, label))
439
488
  return _finalize_completion(
@@ -443,7 +492,7 @@ def workflow_io_ids(ctx, args, incomplete):
443
492
  )
444
493
 
445
494
 
446
- def replay_files(ctx, args, incomplete):
495
+ def replay_files(ctx, param, incomplete):
447
496
  """Prepare a list of JSON replay files."""
448
497
  return file_list(
449
498
  incomplete=incomplete,
@@ -452,9 +501,9 @@ def replay_files(ctx, args, incomplete):
452
501
  )
453
502
 
454
503
 
455
- def installed_package_names(ctx, args, incomplete):
504
+ def installed_package_names(ctx, param, incomplete):
456
505
  """Prepare a list of installed packages."""
457
- CONTEXT.set_connection_from_args(args)
506
+ CONTEXT.set_connection_from_args(ctx.find_root().params)
458
507
  options = []
459
508
  packages = list_packages()
460
509
  for _ in packages:
@@ -471,31 +520,14 @@ def installed_package_names(ctx, args, incomplete):
471
520
  )
472
521
 
473
522
 
474
- def published_package_names(ctx, args, incomplete):
523
+ def published_package_names(ctx, param, incomplete):
475
524
  """List of plugin packages scraped from pypi.org."""
476
525
  options = []
477
- url = "https://pypi.org/search/?q=%22cmem-plugin-%22"
478
- soup = BeautifulSoup(
479
- requests.get(url, timeout=5).content,
480
- "html.parser"
481
- )
482
- packages = soup.find_all("a", class_="package-snippet")
483
- for package in packages:
484
- name = package.findChildren(
485
- class_="package-snippet__name"
486
- )[0].getText()
487
- if name == "cmem-plugin-base":
488
- continue
489
- description = package.findChildren(
490
- class_="package-snippet__description"
491
- )[0].getText()
492
- version = package.findChildren(
493
- class_="package-snippet__version"
494
- )[0].getText()
526
+ for _ in get_published_packages():
495
527
  options.append(
496
528
  (
497
- name,
498
- f"{version}: {description}"
529
+ _.name,
530
+ f"{_.version}: {_.description}"
499
531
  )
500
532
  )
501
533
 
@@ -506,7 +538,7 @@ def published_package_names(ctx, args, incomplete):
506
538
  )
507
539
 
508
540
 
509
- def python_package_files(ctx, args, incomplete):
541
+ def python_package_files(ctx, param, incomplete):
510
542
  """Prepare a list of acceptable python package files."""
511
543
  return file_list(
512
544
  incomplete=incomplete,
@@ -516,14 +548,14 @@ def python_package_files(ctx, args, incomplete):
516
548
  )
517
549
 
518
550
 
519
- def installable_packages(ctx, args, incomplete):
551
+ def installable_packages(ctx, param, incomplete):
520
552
  """Installable packages from files and pypi.org."""
521
553
  return python_package_files(
522
- ctx, args, incomplete) + published_package_names(
523
- ctx, args, incomplete)
554
+ ctx, param, incomplete) + published_package_names(
555
+ ctx, param, incomplete)
524
556
 
525
557
 
526
- def workflow_io_input_files(ctx, args, incomplete):
558
+ def workflow_io_input_files(ctx, param, incomplete):
527
559
  """Prepare a list of acceptable workflow io input files."""
528
560
  return file_list(
529
561
  incomplete=incomplete,
@@ -581,7 +613,7 @@ def workflow_io_input_mimetypes(ctx, args, incomplete):
581
613
  )
582
614
 
583
615
 
584
- def workflow_io_output_files(ctx, args, incomplete):
616
+ def workflow_io_output_files(ctx, param, incomplete):
585
617
  """Prepare a list of acceptable workflow io output files."""
586
618
  return file_list(
587
619
  incomplete=incomplete,
@@ -610,7 +642,7 @@ def workflow_io_output_files(ctx, args, incomplete):
610
642
  )
611
643
 
612
644
 
613
- def dataset_files(ctx, args, incomplete):
645
+ def dataset_files(ctx, param, incomplete):
614
646
  """Prepare a list of SPARQL files."""
615
647
  return file_list(
616
648
  incomplete=incomplete,
@@ -643,7 +675,7 @@ def dataset_files(ctx, args, incomplete):
643
675
  )
644
676
 
645
677
 
646
- def graph_backup_files(ctx, args, incomplete):
678
+ def graph_backup_files(ctx, param, incomplete):
647
679
  """Prepare a list of workspace files."""
648
680
  return file_list(
649
681
  incomplete=incomplete,
@@ -652,7 +684,7 @@ def graph_backup_files(ctx, args, incomplete):
652
684
  )
653
685
 
654
686
 
655
- def project_files(ctx, args, incomplete):
687
+ def project_files(ctx, param, incomplete):
656
688
  """Prepare a list of workspace files."""
657
689
  return file_list(
658
690
  incomplete=incomplete,
@@ -661,7 +693,7 @@ def project_files(ctx, args, incomplete):
661
693
  )
662
694
 
663
695
 
664
- def ini_files(ctx, args, incomplete):
696
+ def ini_files(ctx, param, incomplete):
665
697
  """Prepare a list of workspace files."""
666
698
  return file_list(
667
699
  incomplete=incomplete,
@@ -670,7 +702,7 @@ def ini_files(ctx, args, incomplete):
670
702
  )
671
703
 
672
704
 
673
- def workspace_files(ctx, args, incomplete):
705
+ def workspace_files(ctx, param, incomplete):
674
706
  """Prepare a list of workspace files."""
675
707
  return file_list(
676
708
  incomplete=incomplete,
@@ -679,7 +711,7 @@ def workspace_files(ctx, args, incomplete):
679
711
  )
680
712
 
681
713
 
682
- def sparql_files(ctx, args, incomplete):
714
+ def sparql_files(ctx, param, incomplete):
683
715
  """Prepare a list of SPARQL files."""
684
716
  return file_list(
685
717
  incomplete=incomplete,
@@ -692,7 +724,7 @@ def sparql_files(ctx, args, incomplete):
692
724
  )
693
725
 
694
726
 
695
- def triple_files(ctx, args, incomplete):
727
+ def triple_files(ctx, param, incomplete):
696
728
  """Prepare a list of triple files."""
697
729
  return file_list(
698
730
  incomplete=incomplete,
@@ -705,14 +737,15 @@ def triple_files(ctx, args, incomplete):
705
737
  )
706
738
 
707
739
 
708
- def placeholder(ctx, args, incomplete):
740
+ def placeholder(ctx, param, incomplete):
709
741
  """Prepare a list of placeholder from the to-be executed queries."""
710
742
  # look if cursor is in value position of the -p option and
711
743
  # return nothing in case it is (values are not completed atm)
744
+ args = _get_completion_args(incomplete)
712
745
  if args[len(args) - 2] in ("-p", "--parameter"):
713
746
  return []
714
747
  # setup configuration
715
- CONTEXT.set_connection_from_args(args)
748
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
716
749
  # extract placeholder from given queries in the command line
717
750
  options = []
718
751
  for num, arg in enumerate(args):
@@ -732,12 +765,12 @@ def placeholder(ctx, args, incomplete):
732
765
  )
733
766
 
734
767
 
735
- def remote_queries(ctx, args, incomplete):
768
+ def remote_queries(ctx, param, incomplete):
736
769
  """Prepare a list of query URIs."""
737
- CONTEXT.set_connection_from_args(args)
770
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
738
771
  options = []
739
772
  for _, query in QUERY_CATALOG.get_queries().items():
740
- url = query.short_url.replace(":", r"\:")
773
+ url = query.short_url
741
774
  label = query.label
742
775
  options.append((url, label))
743
776
  return _finalize_completion(
@@ -747,24 +780,24 @@ def remote_queries(ctx, args, incomplete):
747
780
  )
748
781
 
749
782
 
750
- def remote_queries_and_sparql_files(ctx, args, incomplete):
783
+ def remote_queries_and_sparql_files(ctx, param, incomplete):
751
784
  """Prepare a list of named queries, query files and directories."""
752
- remote = remote_queries(ctx, args, incomplete)
753
- files = sparql_files(ctx, args, incomplete)
785
+ remote = remote_queries(ctx, param, incomplete)
786
+ files = sparql_files(ctx, param, incomplete)
754
787
  return remote + files
755
788
 
756
789
 
757
- def workflow_ids(ctx, args, incomplete):
790
+ def workflow_ids(ctx, param, incomplete):
758
791
  """Prepare a list of projectid:taskid workflow identifier."""
759
- CONTEXT.set_connection_from_args(args)
792
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
760
793
  workflows = list_items(item_type="workflow")["results"]
761
794
  options = []
762
795
  for _ in workflows:
763
796
  workflow = _["projectId"] + ":" + _["id"]
764
797
  label = _["label"]
765
- if workflow in args:
798
+ if _ignore_option(workflow, ctx.params):
766
799
  continue
767
- options.append((workflow.replace(":", r"\:"), label))
800
+ options.append((workflow, label))
768
801
  return _finalize_completion(
769
802
  candidates=options,
770
803
  incomplete=incomplete,
@@ -772,19 +805,26 @@ def workflow_ids(ctx, args, incomplete):
772
805
  )
773
806
 
774
807
 
775
- def marshalling_plugins(ctx, args, incomplete):
808
+ def marshalling_plugins(ctx, param, incomplete):
776
809
  """Prepare a list of supported workspace/project import/export plugins."""
777
- CONTEXT.set_connection_from_args(args)
810
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
778
811
  options = get_marshalling_plugins()
779
812
  if "description" in options[0].keys():
780
- return [(_["id"], _["description"]) for _ in options]
781
- # in case, no descriptions are available, labels are fine as well
782
- return [(_["id"], _["label"]) for _ in options]
813
+ final_options = [(_["id"], _["description"]) for _ in options]
814
+ else:
815
+ # in case, no descriptions are available, labels are fine as well
816
+ final_options = [(_["id"], _["label"]) for _ in options]
817
+
818
+ return _finalize_completion(
819
+ candidates=final_options,
820
+ incomplete=incomplete,
821
+ sort_by=SORT_BY_DESC
822
+ )
783
823
 
784
824
 
785
- def project_ids(ctx, args, incomplete):
825
+ def project_ids(ctx, param, incomplete):
786
826
  """Prepare a list of project IDs for auto-completion."""
787
- CONTEXT.set_connection_from_args(args)
827
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
788
828
  try:
789
829
  projects = get_projects()
790
830
  except Exception:
@@ -795,7 +835,7 @@ def project_ids(ctx, args, incomplete):
795
835
  project_id = _["name"]
796
836
  label = _["metaData"]["label"]
797
837
  # do not add project if already in the command line
798
- if project_id in args:
838
+ if _ignore_option(project_id, ctx.params):
799
839
  continue
800
840
  options.append((project_id, label))
801
841
  return _finalize_completion(
@@ -805,9 +845,9 @@ def project_ids(ctx, args, incomplete):
805
845
  )
806
846
 
807
847
 
808
- def graph_uris(ctx, args, incomplete, writeable=True, readonly=True):
848
+ def graph_uris(ctx, param, incomplete, writeable=True, readonly=True):
809
849
  """Prepare a list of graphs for auto-completion."""
810
- CONTEXT.set_connection_from_args(args)
850
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
811
851
  try:
812
852
  graphs = get_graphs()
813
853
  except Exception:
@@ -818,9 +858,9 @@ def graph_uris(ctx, args, incomplete, writeable=True, readonly=True):
818
858
  iri = _["iri"]
819
859
  label = _["label"]["title"]
820
860
  # do not add graph if already in the command line
821
- if iri in args:
861
+ if _ignore_option(iri, ctx.params):
822
862
  continue
823
- options.append((iri.replace(":", r"\:"), label))
863
+ options.append((iri, label))
824
864
  return _finalize_completion(
825
865
  candidates=options,
826
866
  incomplete=incomplete,
@@ -828,15 +868,15 @@ def graph_uris(ctx, args, incomplete, writeable=True, readonly=True):
828
868
  )
829
869
 
830
870
 
831
- def writable_graph_uris(ctx, args, incomplete):
871
+ def writable_graph_uris(ctx, param, incomplete):
832
872
  """Prepare a list of writable graphs for auto-completion."""
833
- return graph_uris(ctx, args, incomplete, writeable=True, readonly=False)
873
+ return graph_uris(ctx, param, incomplete, writeable=True, readonly=False)
834
874
 
835
875
 
836
- def connections(ctx, args, incomplete):
876
+ def connections(ctx, param, incomplete):
837
877
  """Prepare a list of config connections for auto-completion."""
838
878
  # since ctx does not have an obj here, we re-create the object
839
- CONTEXT.set_connection_from_args(args)
879
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
840
880
  options = []
841
881
  for section in CONTEXT.config.sections():
842
882
  options.append(section)
@@ -846,7 +886,7 @@ def connections(ctx, args, incomplete):
846
886
  )
847
887
 
848
888
 
849
- def graph_export_templates(ctx, args, incomplete):
889
+ def graph_export_templates(ctx, param, incomplete):
850
890
  """Prepare a list of example templates for the graph export command."""
851
891
  examples = [
852
892
  (
@@ -872,7 +912,7 @@ def graph_export_templates(ctx, args, incomplete):
872
912
  )
873
913
 
874
914
 
875
- def project_export_templates(ctx, args, incomplete):
915
+ def project_export_templates(ctx, param, incomplete):
876
916
  """Prepare a list of example templates for the project export command."""
877
917
  examples = [
878
918
  (
@@ -893,7 +933,7 @@ def project_export_templates(ctx, args, incomplete):
893
933
  )
894
934
 
895
935
 
896
- def workspace_export_templates(ctx, args, incomplete):
936
+ def workspace_export_templates(ctx, param, incomplete):
897
937
  """Prepare a list of example templates for the workspace export command."""
898
938
  examples = [
899
939
  (
@@ -914,7 +954,7 @@ def workspace_export_templates(ctx, args, incomplete):
914
954
  )
915
955
 
916
956
 
917
- def query_status_filter(ctx, args, incomplete):
957
+ def query_status_filter(ctx, param, incomplete):
918
958
  """Prepare a list of filter names and values for query status filter."""
919
959
  filter_names = [
920
960
  (
@@ -1035,7 +1075,7 @@ def query_status_filter(ctx, args, incomplete):
1035
1075
  "Queries of unknown type."
1036
1076
  )
1037
1077
  ]
1038
-
1078
+ args = _get_completion_args(incomplete)
1039
1079
  last_argument = args[len(args) - 1]
1040
1080
  options = None
1041
1081
  if last_argument == "--filter":
@@ -1065,11 +1105,11 @@ def query_status_filter(ctx, args, incomplete):
1065
1105
  incomplete=incomplete
1066
1106
  )
1067
1107
  if last_argument == "user":
1068
- options = query_account_iris(ctx, args, incomplete)
1108
+ options = query_account_iris(ctx, param, incomplete)
1069
1109
  if last_argument == "trace-id":
1070
- options = query_trace_ids(ctx, args, incomplete)
1110
+ options = query_trace_ids(ctx, param, incomplete)
1071
1111
  if last_argument == "graph":
1072
- options = query_graphs(ctx, args, incomplete)
1112
+ options = query_graphs(ctx, param, incomplete)
1073
1113
 
1074
1114
  if not options:
1075
1115
  raise ValueError(
@@ -1078,9 +1118,9 @@ def query_status_filter(ctx, args, incomplete):
1078
1118
  return options
1079
1119
 
1080
1120
 
1081
- def query_account_iris(ctx, args, incomplete):
1121
+ def query_account_iris(ctx, param, incomplete):
1082
1122
  """Prepare a list account IRIs from the query status."""
1083
- CONTEXT.set_connection_from_args(args)
1123
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1084
1124
  accounts = {}
1085
1125
  for _ in get_query_status():
1086
1126
  if _["user"] in accounts:
@@ -1088,7 +1128,7 @@ def query_account_iris(ctx, args, incomplete):
1088
1128
  else:
1089
1129
  accounts[_["user"]] = 1
1090
1130
  options = [
1091
- (account.replace(":", r"\:"), f"{count} queries")
1131
+ (account, f"{count} queries")
1092
1132
  for account, count in accounts.items()
1093
1133
  ]
1094
1134
  return _finalize_completion(
@@ -1097,9 +1137,9 @@ def query_account_iris(ctx, args, incomplete):
1097
1137
  )
1098
1138
 
1099
1139
 
1100
- def query_trace_ids(ctx, args, incomplete):
1140
+ def query_trace_ids(ctx, param, incomplete):
1101
1141
  """Prepare a list trace IDs from the query status."""
1102
- CONTEXT.set_connection_from_args(args)
1142
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1103
1143
  options = Counter(
1104
1144
  [query["traceId"] for query in get_query_status()]
1105
1145
  ).most_common()
@@ -1110,9 +1150,9 @@ def query_trace_ids(ctx, args, incomplete):
1110
1150
  )
1111
1151
 
1112
1152
 
1113
- def query_graphs(ctx, args, incomplete):
1153
+ def query_graphs(ctx, param, incomplete):
1114
1154
  """Prepare a list graph URLs from the query status."""
1115
- CONTEXT.set_connection_from_args(args)
1155
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1116
1156
  options = Counter()
1117
1157
  for query in get_query_status():
1118
1158
  for graph in query.get("affectedGraphs", []):
@@ -1124,7 +1164,7 @@ def query_graphs(ctx, args, incomplete):
1124
1164
  )
1125
1165
 
1126
1166
 
1127
- def graph_list_filter(ctx, args, incomplete):
1167
+ def graph_list_filter(ctx, param, incomplete):
1128
1168
  """Prepare a list of filter names and values for graph list filter."""
1129
1169
  filter_names = [
1130
1170
  (
@@ -1148,7 +1188,7 @@ def graph_list_filter(ctx, args, incomplete):
1148
1188
  "List only graphs which ARE writeable for the current user."
1149
1189
  )
1150
1190
  ]
1151
-
1191
+ args = _get_completion_args(incomplete)
1152
1192
  options = []
1153
1193
  if args[len(args) - 1] == "--filter":
1154
1194
  options = _finalize_completion(
@@ -1161,11 +1201,72 @@ def graph_list_filter(ctx, args, incomplete):
1161
1201
  incomplete=incomplete
1162
1202
  )
1163
1203
  if args[len(args) - 1] == "imported-by":
1164
- options = graph_uris(ctx, args, incomplete)
1204
+ options = graph_uris(ctx, param, incomplete)
1165
1205
  return options
1166
1206
 
1167
1207
 
1168
- def resource_list_filter(ctx, args, incomplete):
1208
+ def variable_ids(ctx, param, incomplete):
1209
+ """Prepare a list of variables IDs for auto-completion."""
1210
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1211
+ try:
1212
+ variables = get_all_variables()
1213
+ except Exception:
1214
+ # if something went wrong, die silently
1215
+ return []
1216
+ options = []
1217
+ for _ in variables:
1218
+ variable_id = _["id"]
1219
+ label = _.get("description", "").partition('\n')[0]
1220
+ if label == "":
1221
+ label = f"Current value: {_['value']}"
1222
+ # do not add project if already in the command line
1223
+ if _ignore_option(variable_id, ctx.params):
1224
+ continue
1225
+ options.append((variable_id, label))
1226
+ return _finalize_completion(
1227
+ candidates=options,
1228
+ incomplete=incomplete,
1229
+ sort_by=SORT_BY_KEY
1230
+ )
1231
+
1232
+
1233
+ def variable_list_filter(ctx, param, incomplete):
1234
+ """Prepare a list of filter names and values for variable list filter."""
1235
+ filter_names = [
1236
+ (
1237
+ "project",
1238
+ "Filter for variables from a specific project."
1239
+ ),
1240
+ (
1241
+ "regex",
1242
+ "Filter for variables with a regular expression search over "
1243
+ "id, value and description."
1244
+ )
1245
+ ]
1246
+ filter_values_regex = [
1247
+ (
1248
+ "ending$",
1249
+ "Variables name ends with 'ending'."
1250
+ ),
1251
+ (
1252
+ "^starting",
1253
+ "Variables name starts with 'starting'."
1254
+ )
1255
+ ]
1256
+ args = _get_completion_args(incomplete)
1257
+ if args[len(args) - 1] == "--filter":
1258
+ return [CompletionItem(value=f[0], help=f[1]) for f in filter_names]
1259
+ if args[len(args) - 1] == "regex":
1260
+ return _finalize_completion(
1261
+ candidates=filter_values_regex,
1262
+ incomplete=incomplete
1263
+ )
1264
+ if args[len(args) - 1] == "project":
1265
+ return project_ids(ctx, param, incomplete)
1266
+ return []
1267
+
1268
+
1269
+ def resource_list_filter(ctx, param, incomplete):
1169
1270
  """Prepare a list of filter names and values for resource list filter."""
1170
1271
  filter_names = [
1171
1272
  (
@@ -1188,12 +1289,12 @@ def resource_list_filter(ctx, args, incomplete):
1188
1289
  "File resources which name has a date from 2021-10 in it"
1189
1290
  )
1190
1291
  ]
1191
-
1292
+ args = _get_completion_args(incomplete)
1192
1293
  if args[len(args) - 1] == "--filter":
1193
- return filter_names
1294
+ return [CompletionItem(value=f[0], help=f[1]) for f in filter_names]
1194
1295
  if args[len(args) - 1] == "project":
1195
1296
  return _finalize_completion(
1196
- candidates=project_ids(ctx, args, incomplete),
1297
+ candidates=project_ids(ctx, param, incomplete),
1197
1298
  incomplete=incomplete
1198
1299
  )
1199
1300
  if args[len(args) - 1] == "regex":
@@ -1204,7 +1305,7 @@ def resource_list_filter(ctx, args, incomplete):
1204
1305
  return []
1205
1306
 
1206
1307
 
1207
- def workflow_list_filter(ctx, args, incomplete):
1308
+ def workflow_list_filter(ctx, param, incomplete):
1208
1309
  """Prepare a list of filter names and values for workflow list filter."""
1209
1310
  filter_names = [
1210
1311
  (
@@ -1253,23 +1354,28 @@ def workflow_list_filter(ctx, args, incomplete):
1253
1354
  )
1254
1355
  ]
1255
1356
  options = []
1357
+ args = _get_completion_args(incomplete)
1256
1358
  if args[len(args) - 1] == "--filter":
1257
1359
  options = filter_names
1258
1360
  if args[len(args) - 1] == "io":
1259
1361
  options = filter_values_io
1260
1362
  if args[len(args) - 1] == "project":
1261
- options = project_ids(ctx, args, incomplete)
1363
+ options = project_ids(ctx, param, incomplete)
1262
1364
  if args[len(args) - 1] == "tag":
1263
- options = tag_labels(ctx, args, incomplete, "workflow")
1365
+ options = tag_labels(ctx, param, incomplete, "workflow")
1264
1366
  if args[len(args) - 1] == "regex":
1265
1367
  options = filter_regex
1368
+
1369
+ if len(options) > 0 and isinstance(options[0], CompletionItem):
1370
+ return options
1371
+
1266
1372
  return _finalize_completion(
1267
1373
  candidates=options,
1268
1374
  incomplete=incomplete
1269
1375
  )
1270
1376
 
1271
1377
 
1272
- def tag_labels(ctx, args, incomplete, item_type):
1378
+ def tag_labels(ctx, param, incomplete, item_type):
1273
1379
  """Prepare a list of tag labels for a item_type."""
1274
1380
  datasets = list_items(item_type=item_type)
1275
1381
  options = []
@@ -1290,7 +1396,7 @@ def tag_labels(ctx, args, incomplete, item_type):
1290
1396
  )
1291
1397
 
1292
1398
 
1293
- def status_keys(ctx, args, incomplete):
1399
+ def status_keys(ctx, param, incomplete):
1294
1400
  """Prepare a list of status keys for the admin status command."""
1295
1401
  options = ["all"]
1296
1402
  os.environ["CMEMPY_IS_CHATTY"] = "false"
@@ -1303,9 +1409,9 @@ def status_keys(ctx, args, incomplete):
1303
1409
  )
1304
1410
 
1305
1411
 
1306
- def user_ids(ctx, args, incomplete):
1412
+ def user_ids(ctx, param, incomplete):
1307
1413
  """Prepare a list of username for admin update/delete/password command."""
1308
- CONTEXT.set_connection_from_args(args)
1414
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1309
1415
  options = []
1310
1416
  for _ in list_users():
1311
1417
  options.append(
@@ -1320,17 +1426,17 @@ def user_ids(ctx, args, incomplete):
1320
1426
  )
1321
1427
 
1322
1428
 
1323
- def user_group_ids(ctx: Context, args, incomplete):
1429
+ def user_group_ids(ctx: Context, param, incomplete):
1324
1430
  """Prepare a list of group name for admin user update
1325
1431
  --unassign-group/--assign-group parameter"""
1326
- CONTEXT.set_connection_from_args(args)
1432
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1327
1433
  if not ctx.args:
1328
1434
  return []
1329
1435
  users = get_user_by_username(username=str(ctx.args[0]))
1330
1436
  if not users:
1331
1437
  return []
1332
1438
 
1333
- if args[len(args) - 1] == "--unassign-group":
1439
+ if param.name == "unassign_group":
1334
1440
  groups = user_groups(user_id=users[0]["id"])
1335
1441
  else:
1336
1442
  user_group_names = (
@@ -1346,10 +1452,14 @@ def user_group_ids(ctx: Context, args, incomplete):
1346
1452
  _["name"]
1347
1453
  )
1348
1454
  )
1349
- for num, arg in enumerate(args):
1350
- if num - 1 > 0 and args[num - 1] == args[len(args) - 1]:
1351
- with suppress(ValueError):
1352
- options.remove(arg)
1455
+
1456
+ for arg in ctx.params["assign_group"]:
1457
+ with suppress(ValueError):
1458
+ options.remove(arg)
1459
+ for arg in ctx.params["unassign_group"]:
1460
+ with suppress(ValueError):
1461
+ options.remove(arg)
1462
+
1353
1463
  return _finalize_completion(
1354
1464
  candidates=options,
1355
1465
  incomplete=incomplete,
@@ -1357,9 +1467,9 @@ def user_group_ids(ctx: Context, args, incomplete):
1357
1467
  )
1358
1468
 
1359
1469
 
1360
- def client_ids(ctx, args, incomplete):
1470
+ def client_ids(ctx, param, incomplete):
1361
1471
  """Prepare a list of client ids for admin secret and update command."""
1362
- CONTEXT.set_connection_from_args(args)
1472
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1363
1473
  options = []
1364
1474
  for _ in list_open_id_clients():
1365
1475
  options.append(
@@ -1374,16 +1484,16 @@ def client_ids(ctx, args, incomplete):
1374
1484
  )
1375
1485
 
1376
1486
 
1377
- def transformation_task_ids(ctx, args, incomplete):
1487
+ def transformation_task_ids(ctx, param, incomplete):
1378
1488
  """Prepare a list of projectId:transformation task identifier."""
1379
- CONTEXT.set_connection_from_args(args)
1489
+ CONTEXT.set_connection_from_params(ctx.find_root().params)
1380
1490
  options = []
1381
1491
  results = list_items(item_type="transform")
1382
1492
  datasets = results["results"]
1383
1493
  for _ in datasets:
1384
1494
  options.append(
1385
1495
  (
1386
- _["projectId"] + r"\:" + _["id"],
1496
+ _["projectId"] + ":" + _["id"],
1387
1497
  _["label"]
1388
1498
  )
1389
1499
  )