meerschaum 2.4.11__py3-none-any.whl → 2.4.13__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.
Files changed (41) hide show
  1. meerschaum/_internal/arguments/_parse_arguments.py +15 -1
  2. meerschaum/_internal/docs/index.py +1 -0
  3. meerschaum/_internal/entry.py +1 -0
  4. meerschaum/_internal/shell/Shell.py +19 -9
  5. meerschaum/_internal/shell/ShellCompleter.py +11 -6
  6. meerschaum/actions/bootstrap.py +119 -16
  7. meerschaum/actions/clear.py +41 -30
  8. meerschaum/actions/delete.py +1 -1
  9. meerschaum/actions/edit.py +118 -3
  10. meerschaum/actions/sh.py +11 -10
  11. meerschaum/actions/start.py +61 -4
  12. meerschaum/actions/sync.py +14 -16
  13. meerschaum/actions/upgrade.py +5 -4
  14. meerschaum/api/dash/callbacks/dashboard.py +2 -1
  15. meerschaum/api/dash/callbacks/jobs.py +53 -7
  16. meerschaum/api/dash/callbacks/pipes.py +1 -1
  17. meerschaum/api/dash/jobs.py +86 -60
  18. meerschaum/api/dash/pages/__init__.py +1 -0
  19. meerschaum/api/dash/pages/job.py +21 -0
  20. meerschaum/api/routes/_jobs.py +3 -3
  21. meerschaum/config/_version.py +1 -1
  22. meerschaum/connectors/sql/_fetch.py +67 -61
  23. meerschaum/connectors/sql/_pipes.py +36 -29
  24. meerschaum/plugins/__init__.py +6 -2
  25. meerschaum/plugins/bootstrap.py +15 -15
  26. meerschaum/utils/formatting/__init__.py +32 -16
  27. meerschaum/utils/formatting/_pipes.py +1 -1
  28. meerschaum/utils/formatting/_shell.py +4 -3
  29. meerschaum/utils/process.py +18 -8
  30. meerschaum/utils/prompt.py +16 -15
  31. meerschaum/utils/sql.py +107 -35
  32. meerschaum/utils/venv/__init__.py +35 -24
  33. meerschaum/utils/warnings.py +7 -7
  34. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/METADATA +1 -1
  35. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/RECORD +41 -40
  36. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/WHEEL +1 -1
  37. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/LICENSE +0 -0
  38. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/NOTICE +0 -0
  39. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/entry_points.txt +0 -0
  40. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/top_level.txt +0 -0
  41. {meerschaum-2.4.11.dist-info → meerschaum-2.4.13.dist-info}/zip-safe +0 -0
@@ -256,7 +256,8 @@ def parse_synonyms(
256
256
 
257
257
 
258
258
  def parse_dict_to_sysargs(
259
- args_dict: Dict[str, Any]
259
+ args_dict: Dict[str, Any],
260
+ coerce_dates: bool = True,
260
261
  ) -> List[str]:
261
262
  """Revert an arguments dictionary back to a command line list."""
262
263
  import shlex
@@ -304,6 +305,19 @@ def parse_dict_to_sysargs(
304
305
  if len(args_dict[a]) > 0:
305
306
  sysargs += [t[0], json.dumps(args_dict[a], separators=(',', ':'))]
306
307
 
308
+ ### Preserve the original datetime strings if possible
309
+ elif a in ('begin', 'end') and 'sysargs' in args_dict:
310
+ flag = t[0]
311
+ flag_ix = args_dict['sysargs'].index(flag)
312
+ if flag_ix < 0:
313
+ continue
314
+ try:
315
+ flag_val = args_dict['sysargs'][flag_ix + 1]
316
+ except IndexError:
317
+ flag_val = str(args_dict[a])
318
+
319
+ sysargs += [flag, str(flag_val)]
320
+
307
321
  ### Account for None and other values
308
322
  elif (args_dict[a] is not None) or (args_dict[a] is None and a in allow_none_args):
309
323
  sysargs += [t[0], str(args_dict[a])]
@@ -742,6 +742,7 @@ def init_dash(dash_app):
742
742
  <li><code>meerschaum.utils.sql.get_db_version()</code></li>
743
743
  <li><code>meerschaum.utils.sql.get_rename_table_queries()</code></li>
744
744
  <li><code>meerschaum.utils.sql.get_create_table_query()</code></li>
745
+ <li><code>meerschaum.utils.sql.wrap_query_with_cte()</code></li>
745
746
  <li><code>meerschaum.utils.sql.format_cte_subquery()</code></li>
746
747
  <li><code>meerschaum.utils.sql.session_execute()</code></li>
747
748
  </ul>
@@ -43,6 +43,7 @@ if (_STATIC_CONFIG['environment']['systemd_log_path']) in os.environ:
43
43
  if _systemd_stdin_path:
44
44
  sys.stdin = _StdinFile(_systemd_stdin_path)
45
45
 
46
+
46
47
  def entry(
47
48
  sysargs: Optional[List[str]] = None,
48
49
  _patch_args: Optional[Dict[str, Any]] = None,
@@ -235,6 +235,23 @@ def get_shell_intro(with_color: bool = True) -> str:
235
235
  **get_config('shell', 'ansi', 'intro', 'rich')
236
236
  )
237
237
 
238
+ def get_shell_session():
239
+ """
240
+ Return the `prompt_toolkit` prompt session.
241
+ """
242
+ from meerschaum.config._paths import SHELL_HISTORY_PATH
243
+ if 'session' in shell_attrs:
244
+ return shell_attrs['session']
245
+
246
+ shell_attrs['session'] = prompt_toolkit_shortcuts.PromptSession(
247
+ history=prompt_toolkit_history.FileHistory(SHELL_HISTORY_PATH.as_posix()),
248
+ auto_suggest=ValidAutoSuggest(),
249
+ completer=ShellCompleter(),
250
+ complete_while_typing=True,
251
+ reserve_space_for_menu=False,
252
+ )
253
+ return shell_attrs['session']
254
+
238
255
 
239
256
  class Shell(cmd.Cmd):
240
257
  """
@@ -264,14 +281,7 @@ class Shell(cmd.Cmd):
264
281
  except AttributeError:
265
282
  pass
266
283
 
267
- from meerschaum.config._paths import SHELL_HISTORY_PATH
268
- shell_attrs['session'] = prompt_toolkit_shortcuts.PromptSession(
269
- history=prompt_toolkit_history.FileHistory(SHELL_HISTORY_PATH.as_posix()),
270
- auto_suggest=ValidAutoSuggest(),
271
- completer=ShellCompleter(),
272
- complete_while_typing=True,
273
- reserve_space_for_menu=False,
274
- )
284
+ _ = get_shell_session()
275
285
 
276
286
  super().__init__()
277
287
 
@@ -626,7 +636,7 @@ class Shell(cmd.Cmd):
626
636
  step_action_name = step_action[0] if step_action else None
627
637
  ### NOTE: For `stack`, revert argument parsing.
628
638
  step_sysargs = (
629
- parse_dict_to_sysargs(step_kwargs)
639
+ parse_dict_to_sysargs(step_kwargs, coerce_dates=False)
630
640
  if step_action_name != 'stack'
631
641
  else chained_sysargs[i]
632
642
  )
@@ -31,15 +31,18 @@ class ShellCompleter(Completer):
31
31
  ensure_readline()
32
32
  parts = document.text.split('-')
33
33
  ends_with_space = parts[0].endswith(' ')
34
- part_0_subbed_spaces = parts[0].replace(' ', '_')
35
- parsed_text = part_0_subbed_spaces + '-'.join(parts[1:])
34
+ last_action_line = parts[0].split('+')[-1]
35
+ part_0_subbed_spaces = last_action_line.replace(' ', '_')
36
+ parsed_text = (part_0_subbed_spaces + '-'.join(parts[1:]))
36
37
 
38
+ if not parsed_text:
39
+ return
37
40
 
38
41
  ### Index is the rank order (0 is closest match).
39
42
  ### Break when no results are returned.
40
43
  for i, a in enumerate(shell_actions):
41
44
  try:
42
- poss = shell.complete(parsed_text, i)
45
+ poss = shell.complete(parsed_text.lstrip('_'), i)
43
46
  if poss:
44
47
  poss = poss.replace('_', ' ')
45
48
  ### Having issues with readline on portable Windows.
@@ -50,7 +53,9 @@ class ShellCompleter(Completer):
50
53
  yield Completion(poss, start_position=(-1 * len(poss)))
51
54
  yielded.append(poss)
52
55
 
53
- args = parse_line(document.text)
56
+ line = document.text
57
+ current_action_line = line.split('+')[-1].lstrip()
58
+ args = parse_line(current_action_line)
54
59
  action_function = get_action(args['action'], _actions=shell_attrs.get('_actions', None))
55
60
  if action_function is None:
56
61
  return
@@ -74,8 +79,8 @@ class ShellCompleter(Completer):
74
79
  shell,
75
80
  complete_function_name
76
81
  )(
77
- document.text.split(' ')[-1],
78
- document.text,
82
+ current_action_line.split(' ')[-1],
83
+ current_action_line,
79
84
  0,
80
85
  0
81
86
  )
@@ -8,15 +8,18 @@ Functions for bootstrapping elements
8
8
  """
9
9
 
10
10
  from __future__ import annotations
11
+
12
+ import meerschaum as mrsm
11
13
  from meerschaum.utils.typing import Union, Any, Sequence, SuccessTuple, Optional, Tuple, List
12
14
 
15
+
13
16
  def bootstrap(
14
- action: Optional[List[str]] = None,
15
- **kw: Any
16
- ) -> SuccessTuple:
17
+ action: Optional[List[str]] = None,
18
+ **kw: Any
19
+ ) -> SuccessTuple:
17
20
  """
18
21
  Launch an interactive wizard to bootstrap pipes or connectors.
19
-
22
+
20
23
  Example:
21
24
  `bootstrap pipes`
22
25
 
@@ -26,23 +29,24 @@ def bootstrap(
26
29
  'pipes' : _bootstrap_pipes,
27
30
  'connectors' : _bootstrap_connectors,
28
31
  'plugins' : _bootstrap_plugins,
32
+ 'jobs' : _bootstrap_jobs,
29
33
  }
30
34
  return choose_subaction(action, options, **kw)
31
35
 
32
36
 
33
37
  def _bootstrap_pipes(
34
- action: Optional[List[str]] = None,
35
- connector_keys: Optional[List[str]] = None,
36
- metric_keys: Optional[List[str]] = None,
37
- location_keys: Optional[List[Optional[str]]] = None,
38
- yes: bool = False,
39
- force: bool = False,
40
- noask: bool = False,
41
- debug: bool = False,
42
- mrsm_instance: Optional[str] = None,
43
- shell: bool = False,
44
- **kw: Any
45
- ) -> SuccessTuple:
38
+ action: Optional[List[str]] = None,
39
+ connector_keys: Optional[List[str]] = None,
40
+ metric_keys: Optional[List[str]] = None,
41
+ location_keys: Optional[List[Optional[str]]] = None,
42
+ yes: bool = False,
43
+ force: bool = False,
44
+ noask: bool = False,
45
+ debug: bool = False,
46
+ mrsm_instance: Optional[str] = None,
47
+ shell: bool = False,
48
+ **kw: Any
49
+ ) -> SuccessTuple:
46
50
  """
47
51
  Create a new pipe.
48
52
  If no keys are provided, guide the user through the steps required.
@@ -433,6 +437,105 @@ def _bootstrap_plugins(
433
437
  return True, "Success"
434
438
 
435
439
 
440
+ def _bootstrap_jobs(
441
+ action: Optional[List[str]] = None,
442
+ executor_keys: Optional[str] = None,
443
+ debug: bool = False,
444
+ **kwargs: Any
445
+ ) -> SuccessTuple:
446
+ """
447
+ Launch an interactive wizard to create new jobs.
448
+ """
449
+ import shlex
450
+ from meerschaum.utils.prompt import prompt, yes_no
451
+ from meerschaum.actions import actions
452
+ from meerschaum.utils.formatting import print_options, make_header
453
+ from meerschaum.utils.formatting._shell import clear_screen
454
+ from meerschaum.utils.warnings import info
455
+ from meerschaum._internal.arguments import (
456
+ split_pipeline_sysargs,
457
+ split_chained_sysargs,
458
+ )
459
+ from meerschaum.utils.misc import items_str
460
+ from meerschaum._internal.shell.ShellCompleter import ShellCompleter
461
+
462
+ if not action:
463
+ action = [prompt("What is the name of the job you'd like to create?")]
464
+
465
+ for name in action:
466
+ clear_screen(debug=debug)
467
+ job = mrsm.Job(name, executor_keys=executor_keys)
468
+ if job.exists():
469
+ edit_success, edit_msg = actions['edit'](['job', name], **kwargs)
470
+ if not edit_success:
471
+ return edit_success, edit_msg
472
+ continue
473
+
474
+ info(
475
+ f"Editing arguments for job '{name}'.\n"
476
+ " Press [Esc + Enter] to submit, [CTRL + C] to exit.\n\n"
477
+ " Tip: join multiple actions with `+`, add pipeline arguments with `:`.\n"
478
+ " https://meerschaum.io/reference/actions/#chaining-actions\n"
479
+ )
480
+ try:
481
+ new_sysargs_str = prompt(
482
+ "",
483
+ multiline=True,
484
+ icon=False,
485
+ completer=ShellCompleter(),
486
+ )
487
+ except KeyboardInterrupt:
488
+ return True, "Nothing was changed."
489
+
490
+ new_sysargs = shlex.split(new_sysargs_str)
491
+ new_sysargs, pipeline_args = split_pipeline_sysargs(new_sysargs)
492
+ chained_sysargs = split_chained_sysargs(new_sysargs)
493
+
494
+ clear_screen(debug=debug)
495
+ if len(chained_sysargs) > 1:
496
+ print_options(
497
+ [
498
+ shlex.join(step_sysargs)
499
+ for step_sysargs in chained_sysargs
500
+ ],
501
+ header=f"Steps in Job '{name}':",
502
+ number_options=True,
503
+ **kwargs
504
+ )
505
+ else:
506
+ print('\n' + make_header(f"Action for Job '{name}':"))
507
+ print(shlex.join(new_sysargs))
508
+
509
+ if pipeline_args:
510
+ print('\n' + make_header("Pipeline Arguments:"))
511
+ print(shlex.join(pipeline_args))
512
+ print()
513
+
514
+ if not yes_no(
515
+ (
516
+ f"Are you sure you want to create job '{name}' with the above arguments?\n"
517
+ + " The job will be started if you continue."
518
+ ),
519
+ default='n',
520
+ **kwargs
521
+ ):
522
+ return True, "Nothing was changed."
523
+
524
+ new_job = mrsm.Job(name, new_sysargs_str, executor_keys=executor_keys)
525
+ start_success, start_msg = new_job.start()
526
+ if not start_success:
527
+ return start_success, start_msg
528
+
529
+ msg = (
530
+ "Successfully bootstrapped job"
531
+ + ('s' if len(action) != 1 else '')
532
+ + ' '
533
+ + items_str(action)
534
+ + '.'
535
+ )
536
+ return True, msg
537
+
538
+
436
539
  ### NOTE: This must be the final statement of the module.
437
540
  ### Any subactions added below these lines will not
438
541
  ### be added to the `help` docstring.
@@ -6,12 +6,16 @@ Functions for clearing pipes.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
9
+
10
+ from datetime import datetime
11
+ import meerschaum as mrsm
9
12
  from meerschaum.utils.typing import List, SuccessTuple, Any, Optional
10
13
 
14
+
11
15
  def clear(
12
- action: Optional[List[str]] = None,
13
- **kw: Any
14
- ) -> SuccessTuple:
16
+ action: Optional[List[str]] = None,
17
+ **kw: Any
18
+ ) -> SuccessTuple:
15
19
  """
16
20
  Clear pipes of their data, or clear the screen.
17
21
 
@@ -34,24 +38,25 @@ def clear(
34
38
 
35
39
 
36
40
  def _clear_pipes(
37
- action: Optional[List[str]] = None,
38
- begin: Optional[datetime.datetime] = None,
39
- end: Optional[datetime.datetime] = None,
40
- connector_keys: Optional[List[str]] = None,
41
- metric_keys: Optional[List[str]] = None,
42
- mrsm_instance: Optional[str] = None,
43
- location_keys: Optional[List[str]] = None,
44
- force: bool = False,
45
- debug: bool = False,
46
- **kw: Any
47
- ) -> SuccessTuple:
41
+ action: Optional[List[str]] = None,
42
+ begin: Optional[datetime] = None,
43
+ end: Optional[datetime] = None,
44
+ connector_keys: Optional[List[str]] = None,
45
+ metric_keys: Optional[List[str]] = None,
46
+ mrsm_instance: Optional[str] = None,
47
+ location_keys: Optional[List[str]] = None,
48
+ yes: bool = False,
49
+ force: bool = False,
50
+ debug: bool = False,
51
+ **kw: Any
52
+ ) -> SuccessTuple:
48
53
  """
49
54
  Clear pipes' data without dropping any tables.
50
55
 
51
56
  """
52
57
  from meerschaum import get_pipes
53
58
  from meerschaum.utils.formatting import print_tuple
54
-
59
+
55
60
  successes = {}
56
61
  fails = {}
57
62
 
@@ -61,13 +66,19 @@ def _clear_pipes(
61
66
  )
62
67
 
63
68
  if not force:
64
- if not _ask_with_rowcounts(pipes, begin=begin, end=end, debug=debug, **kw):
69
+ if not _ask_with_rowcounts(pipes, begin=begin, end=end, debug=debug, yes=yes, **kw):
65
70
  return False, "No rows were deleted."
66
71
 
67
72
  for pipe in pipes:
68
- success, msg = pipe.clear(begin=begin, end=end, debug=debug, **kw)
69
- print_tuple((success, msg))
70
- (successes if success else fails)[pipe] = msg
73
+ clear_success, clear_msg = pipe.clear(
74
+ begin=begin,
75
+ end=end,
76
+ yes=yes,
77
+ debug=debug,
78
+ **kw
79
+ )
80
+ print_tuple((clear_success, clear_msg))
81
+ (successes if clear_success else fails)[pipe] = clear_msg
71
82
 
72
83
  success = len(successes) > 0
73
84
  msg = (
@@ -79,15 +90,15 @@ def _clear_pipes(
79
90
 
80
91
 
81
92
  def _ask_with_rowcounts(
82
- pipes: List[meerschaum.Pipe],
83
- begin: Optional[datetime.datetime] = None,
84
- end: Optional[datetime.datetime] = None,
85
- yes: bool = False,
86
- nopretty: bool = False,
87
- noask: bool = False,
88
- debug: bool = False,
89
- **kw
90
- ) -> bool:
93
+ pipes: List[mrsm.Pipe],
94
+ begin: Optional[datetime] = None,
95
+ end: Optional[datetime] = None,
96
+ yes: bool = False,
97
+ nopretty: bool = False,
98
+ noask: bool = False,
99
+ debug: bool = False,
100
+ **kw
101
+ ) -> bool:
91
102
  """
92
103
  Count all of the pipes' rowcounts and confirm with the user that these rows need to be deleted.
93
104
 
@@ -109,13 +120,13 @@ def _ask_with_rowcounts(
109
120
  warn(
110
121
  f"No datetime could be determined for {pipe}!\n"
111
122
  + " THIS WILL DELETE THE ENTIRE TABLE!",
112
- stack = False
123
+ stack=False
113
124
  )
114
125
  else:
115
126
  warn(
116
127
  f"A datetime wasn't specified for {pipe}.\n"
117
128
  + f" Using column \"{_dt}\" for datetime bounds...",
118
- stack = False
129
+ stack=False
119
130
  )
120
131
 
121
132
 
@@ -498,7 +498,7 @@ def _complete_delete_jobs(
498
498
  action: Optional[List[str]] = None,
499
499
  executor_keys: Optional[str] = None,
500
500
  line: str = '',
501
- _get_job_method: Optional[str, List[str]] = None,
501
+ _get_job_method: Union[str, List[str], None] = None,
502
502
  **kw
503
503
  ) -> List[str]:
504
504
  from meerschaum._internal.shell.Shell import shell_attrs
@@ -7,9 +7,11 @@ Functions for editing elements belong here.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
+
10
11
  import meerschaum as mrsm
11
12
  from meerschaum.utils.typing import List, Any, SuccessTuple, Optional, Dict
12
13
 
14
+
13
15
  def edit(
14
16
  action: Optional[List[str]] = None,
15
17
  **kw: Any
@@ -24,6 +26,7 @@ def edit(
24
26
  'definition': _edit_definition,
25
27
  'users' : _edit_users,
26
28
  'plugins' : _edit_plugins,
29
+ 'jobs' : _edit_jobs,
27
30
  }
28
31
  return choose_subaction(action, options, **kw)
29
32
 
@@ -35,10 +38,17 @@ def _complete_edit(
35
38
  """
36
39
  Override the default Meerschaum `complete_` function.
37
40
  """
41
+ from meerschaum.actions.delete import _complete_delete_jobs
42
+
43
+ if action is None:
44
+ action = []
45
+
38
46
  options = {
39
47
  'config': _complete_edit_config,
40
48
  'plugin': _complete_edit_plugins,
41
49
  'plugins': _complete_edit_plugins,
50
+ 'job': _complete_delete_jobs,
51
+ 'jobs': _complete_delete_jobs,
42
52
  }
43
53
 
44
54
  if action is None:
@@ -130,7 +140,7 @@ def _edit_pipes(
130
140
 
131
141
  pipes = get_pipes(debug=debug, as_list=True, **kw)
132
142
  if pipes:
133
- print_options(pipes, header=f'Pipes to be edited:')
143
+ print_options(pipes, header='Pipes to be edited:')
134
144
  else:
135
145
  return False, "No pipes to edit."
136
146
 
@@ -141,10 +151,10 @@ def _edit_pipes(
141
151
  f"Press [Enter] to begin editing the above {len(pipes)} pipe"
142
152
  + ("s" if len(pipes) != 1 else "")
143
153
  + " or [CTRL-C] to cancel:",
144
- icon = False,
154
+ icon=False,
145
155
  )
146
156
  except KeyboardInterrupt:
147
- return False, f"No pipes changed."
157
+ return False, "No pipes changed."
148
158
 
149
159
  interactive = (not bool(params))
150
160
  success, msg = True, ""
@@ -372,6 +382,111 @@ def _complete_edit_plugins(
372
382
  return possibilities
373
383
 
374
384
 
385
+ def _edit_jobs(
386
+ action: Optional[List[str]] = None,
387
+ executor_keys: Optional[str] = None,
388
+ debug: bool = False,
389
+ **kwargs: Any
390
+ ) -> mrsm.SuccessTuple:
391
+ """
392
+ Edit existing jobs.
393
+ """
394
+ import shlex
395
+ from meerschaum.jobs import get_filtered_jobs
396
+ from meerschaum.utils.prompt import prompt, yes_no
397
+ from meerschaum._internal.arguments import (
398
+ split_pipeline_sysargs,
399
+ split_chained_sysargs,
400
+ )
401
+ from meerschaum.utils.formatting import make_header, print_options
402
+ from meerschaum.utils.warnings import info
403
+ from meerschaum._internal.shell.ShellCompleter import ShellCompleter
404
+ from meerschaum.utils.misc import items_str
405
+ from meerschaum.utils.formatting._shell import clear_screen
406
+
407
+ jobs = get_filtered_jobs(executor_keys, action, debug=debug)
408
+ if not jobs:
409
+ return False, "No jobs to edit."
410
+
411
+ num_edited = 0
412
+ for name, job in jobs.items():
413
+ sysargs_str = shlex.join(job.sysargs)
414
+ clear_screen(debug=debug)
415
+ info(
416
+ f"Editing arguments for job '{name}'.\n"
417
+ " Press [Esc + Enter] to submit, [CTRL + C] to exit.\n\n"
418
+ " Tip: join actions with `+`, manage pipeline with `:`.\n"
419
+ " https://meerschaum.io/reference/actions/#chaining-actions\n"
420
+ )
421
+
422
+ try:
423
+ new_sysargs_str = prompt(
424
+ "",
425
+ default_editable=sysargs_str.lstrip().rstrip().replace(' + ', '\n+ '),
426
+ multiline=True,
427
+ icon=False,
428
+ completer=ShellCompleter(),
429
+ )
430
+ except KeyboardInterrupt:
431
+ return True, "Nothing was changed."
432
+
433
+ if new_sysargs_str.strip() == sysargs_str.strip():
434
+ continue
435
+
436
+ new_sysargs = shlex.split(new_sysargs_str)
437
+ new_sysargs, pipeline_args = split_pipeline_sysargs(new_sysargs)
438
+ chained_sysargs = split_chained_sysargs(new_sysargs)
439
+
440
+ clear_screen(debug=debug)
441
+ if len(chained_sysargs) > 1:
442
+ print_options(
443
+ [
444
+ shlex.join(step_sysargs)
445
+ for step_sysargs in chained_sysargs
446
+ ],
447
+ header=f"\nSteps in Job '{name}':",
448
+ number_options=True,
449
+ **kwargs
450
+ )
451
+ else:
452
+ print('\n' + make_header(f"Action for Job '{name}':"))
453
+ print(shlex.join(new_sysargs))
454
+
455
+ if pipeline_args:
456
+ print('\n' + make_header("Pipeline Arguments:"))
457
+ print(shlex.join(pipeline_args))
458
+ print()
459
+
460
+ if not yes_no(
461
+ (
462
+ f"Are you sure you want to recreate job '{name}' with the above arguments?\n"
463
+ + " The job will be started if you continue."
464
+ ),
465
+ default='n',
466
+ **kwargs
467
+ ):
468
+ return True, "Nothing was changed."
469
+
470
+ delete_success, delete_msg = job.delete()
471
+ if not delete_success:
472
+ return delete_success, delete_msg
473
+
474
+ new_job = mrsm.Job(name, new_sysargs_str, executor_keys=executor_keys)
475
+ start_success, start_msg = new_job.start()
476
+ if not start_success:
477
+ return start_success, start_msg
478
+ num_edited += 1
479
+
480
+ msg = (
481
+ "Successfully edit job"
482
+ + ('s' if len(jobs) != 1 else '')
483
+ + ' '
484
+ + items_str(list(jobs.keys()))
485
+ + '.'
486
+ ) if num_edited > 0 else "Nothing was edited."
487
+ return True, msg
488
+
489
+
375
490
  ### NOTE: This must be the final statement of the module.
376
491
  ### Any subactions added below these lines will not
377
492
  ### be added to the `help` docstring.
meerschaum/actions/sh.py CHANGED
@@ -9,14 +9,15 @@ NOTE: This action may be a huge security vulnerability
9
9
 
10
10
  from meerschaum.utils.typing import SuccessTuple, List, Any, Optional
11
11
 
12
+
12
13
  def sh(
13
- action: Optional[List[str]] = None,
14
- sub_args: Optional[List[str]] = None,
15
- filtered_sysargs: Optional[List[str]] = None,
16
- use_bash: bool = True,
17
- debug: bool = False,
18
- **kw: Any
19
- ) -> SuccessTuple:
14
+ action: Optional[List[str]] = None,
15
+ sub_args: Optional[List[str]] = None,
16
+ filtered_sysargs: Optional[List[str]] = None,
17
+ use_bash: bool = True,
18
+ debug: bool = False,
19
+ **kw: Any
20
+ ) -> SuccessTuple:
20
21
  """
21
22
  Execute system commands.
22
23
  """
@@ -50,7 +51,7 @@ def sh(
50
51
  if len(action) != 0:
51
52
  try:
52
53
  command_list += ["-c", shlex.join(cmd_list)]
53
- except Exception as e:
54
+ except Exception:
54
55
  command_list += ["-c", ' '.join(cmd_list)]
55
56
  else:
56
57
  if len(action) == 0:
@@ -64,8 +65,8 @@ def sh(
64
65
  try:
65
66
  process = subprocess.Popen(
66
67
  command_list,
67
- shell = False,
68
- env = os.environ,
68
+ shell=False,
69
+ env=os.environ,
69
70
  )
70
71
  exit_code = process.wait()
71
72
  except FileNotFoundError: