meerschaum 2.2.6__py3-none-any.whl → 2.3.0.dev1__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 (61) hide show
  1. meerschaum/__init__.py +4 -1
  2. meerschaum/__main__.py +10 -5
  3. meerschaum/_internal/arguments/_parser.py +44 -15
  4. meerschaum/_internal/entry.py +35 -14
  5. meerschaum/_internal/shell/Shell.py +155 -53
  6. meerschaum/_internal/shell/updates.py +175 -0
  7. meerschaum/actions/api.py +12 -12
  8. meerschaum/actions/attach.py +95 -0
  9. meerschaum/actions/delete.py +35 -26
  10. meerschaum/actions/register.py +19 -5
  11. meerschaum/actions/show.py +119 -148
  12. meerschaum/actions/start.py +85 -75
  13. meerschaum/actions/stop.py +68 -39
  14. meerschaum/actions/sync.py +3 -3
  15. meerschaum/actions/upgrade.py +28 -36
  16. meerschaum/api/_events.py +18 -1
  17. meerschaum/api/_oauth2.py +2 -0
  18. meerschaum/api/_websockets.py +2 -2
  19. meerschaum/api/dash/jobs.py +5 -2
  20. meerschaum/api/routes/__init__.py +1 -0
  21. meerschaum/api/routes/_actions.py +122 -44
  22. meerschaum/api/routes/_jobs.py +340 -0
  23. meerschaum/api/routes/_pipes.py +25 -25
  24. meerschaum/config/_default.py +1 -0
  25. meerschaum/config/_formatting.py +1 -0
  26. meerschaum/config/_paths.py +5 -0
  27. meerschaum/config/_shell.py +84 -67
  28. meerschaum/config/_version.py +1 -1
  29. meerschaum/config/static/__init__.py +9 -0
  30. meerschaum/connectors/__init__.py +9 -11
  31. meerschaum/connectors/api/APIConnector.py +18 -1
  32. meerschaum/connectors/api/_actions.py +60 -71
  33. meerschaum/connectors/api/_jobs.py +260 -0
  34. meerschaum/connectors/api/_misc.py +1 -1
  35. meerschaum/connectors/api/_request.py +13 -9
  36. meerschaum/connectors/parse.py +23 -7
  37. meerschaum/core/Pipe/_sync.py +3 -0
  38. meerschaum/plugins/__init__.py +89 -5
  39. meerschaum/utils/daemon/Daemon.py +333 -149
  40. meerschaum/utils/daemon/FileDescriptorInterceptor.py +19 -10
  41. meerschaum/utils/daemon/RotatingFile.py +18 -7
  42. meerschaum/utils/daemon/StdinFile.py +110 -0
  43. meerschaum/utils/daemon/__init__.py +40 -27
  44. meerschaum/utils/formatting/__init__.py +83 -37
  45. meerschaum/utils/formatting/_jobs.py +118 -51
  46. meerschaum/utils/formatting/_shell.py +6 -0
  47. meerschaum/utils/jobs/_Job.py +684 -0
  48. meerschaum/utils/jobs/__init__.py +245 -0
  49. meerschaum/utils/misc.py +18 -17
  50. meerschaum/utils/packages/__init__.py +21 -15
  51. meerschaum/utils/packages/_packages.py +2 -2
  52. meerschaum/utils/prompt.py +20 -7
  53. meerschaum/utils/schedule.py +21 -15
  54. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/METADATA +9 -9
  55. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/RECORD +61 -54
  56. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/WHEEL +1 -1
  57. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/LICENSE +0 -0
  58. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/NOTICE +0 -0
  59. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/entry_points.txt +0 -0
  60. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/top_level.txt +0 -0
  61. {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dev1.dist-info}/zip-safe +0 -0
@@ -11,9 +11,9 @@ import meerschaum as mrsm
11
11
  from meerschaum.utils.typing import SuccessTuple, Union, Sequence, Any, Optional, List, Dict, Tuple
12
12
 
13
13
  def show(
14
- action: Optional[List[str]] = None,
15
- **kw: Any
16
- ) -> SuccessTuple:
14
+ action: Optional[List[str]] = None,
15
+ **kw: Any
16
+ ) -> SuccessTuple:
17
17
  """Show elements of a certain type.
18
18
 
19
19
  Command:
@@ -552,16 +552,19 @@ def _complete_show_packages(
552
552
 
553
553
  def _show_jobs(
554
554
  action: Optional[List[str]] = None,
555
+ executor_keys: Optional[str] = None,
555
556
  nopretty: bool = False,
557
+ debug: bool = False,
556
558
  **kw: Any
557
559
  ) -> SuccessTuple:
558
560
  """
559
561
  Show the currently running and stopped jobs.
560
562
  """
561
- from meerschaum.utils.daemon import get_filtered_daemons
563
+ from meerschaum.utils.jobs import get_filtered_jobs
562
564
  from meerschaum.utils.formatting._jobs import pprint_jobs
563
- daemons = get_filtered_daemons(action)
564
- if not daemons:
565
+
566
+ jobs = get_filtered_jobs(executor_keys, action, debug=debug)
567
+ if not jobs:
565
568
  if not action and not nopretty:
566
569
  from meerschaum.utils.warnings import info
567
570
  info('No running or stopped jobs.')
@@ -573,12 +576,14 @@ def _show_jobs(
573
576
  " - start job sync pipes --loop"
574
577
  )
575
578
  return False, "No jobs to show."
576
- pprint_jobs(daemons, nopretty=nopretty)
579
+
580
+ pprint_jobs(jobs, nopretty=nopretty)
577
581
  return True, "Success"
578
582
 
579
583
 
580
584
  def _show_logs(
581
585
  action: Optional[List[str]] = None,
586
+ executor_keys: Optional[str] = None,
582
587
  nopretty: bool = False,
583
588
  **kw
584
589
  ) -> SuccessTuple:
@@ -594,179 +599,145 @@ def _show_logs(
594
599
  `show logs myjob myotherjob`
595
600
  """
596
601
  import os, pathlib, random, asyncio
602
+ from functools import partial
597
603
  from datetime import datetime, timezone
598
604
  from meerschaum.utils.packages import attempt_import, import_rich
599
- from meerschaum.utils.daemon import get_filtered_daemons, Daemon
605
+ from meerschaum.utils.jobs import get_filtered_jobs, Job
600
606
  from meerschaum.utils.warnings import warn, info
601
607
  from meerschaum.utils.formatting import get_console, ANSI, UNICODE
602
608
  from meerschaum.utils.misc import tail
603
609
  from meerschaum.config._paths import LOGS_RESOURCES_PATH
604
610
  from meerschaum.config import get_config
611
+ rich = import_rich()
612
+ rich_text = attempt_import('rich.text')
613
+
605
614
  if not ANSI:
606
615
  info = print
607
616
  colors = get_config('jobs', 'logs', 'colors')
608
617
  timestamp_format = get_config('jobs', 'logs', 'timestamps', 'format')
609
618
  follow_timestamp_format = get_config('jobs', 'logs', 'timestamps', 'follow_format')
610
- daemons = get_filtered_daemons(action)
619
+
620
+ jobs = get_filtered_jobs(executor_keys, action)
611
621
  now = datetime.now(timezone.utc)
612
622
  now_str = now.strftime(timestamp_format)
613
623
  now_follow_str = now.strftime(follow_timestamp_format)
614
624
 
615
- def build_buffer_spaces(daemons) -> Dict[str, str]:
625
+ def build_buffer_spaces(_jobs) -> Dict[str, str]:
616
626
  max_len_id = (
617
- max(len(d.daemon_id) for d in daemons) + 1
618
- ) if daemons else 0
627
+ max(len(name) for name in _jobs) + 1
628
+ ) if _jobs else 0
619
629
  buffer_len = max(
620
630
  get_config('jobs', 'logs', 'min_buffer_len'),
621
631
  max_len_id
622
632
  )
623
633
  return {
624
- d.daemon_id: ''.join([' '] * (buffer_len - len(d.daemon_id)))
625
- for d in daemons
634
+ name: ' ' * (buffer_len - len(name))
635
+ for name in _jobs
626
636
  }
627
637
 
628
- def build_job_colors(daemons, _old_job_colors = None) -> Dict[str, str]:
629
- return {d.daemon_id: colors[i % len(colors)] for i, d in enumerate(daemons)}
630
-
631
- buffer_spaces = build_buffer_spaces(daemons)
632
- job_colors = build_job_colors(daemons)
633
-
634
- def get_buffer_spaces(daemon_id):
635
- nonlocal buffer_spaces, daemons
636
- if daemon_id not in buffer_spaces:
637
- d = Daemon(daemon_id=daemon_id)
638
- if d not in daemons:
639
- daemons = get_filtered_daemons(action)
640
- buffer_spaces = build_buffer_spaces(daemons)
641
- return buffer_spaces[daemon_id] or ' '
642
-
643
- def get_job_colors(daemon_id):
644
- nonlocal job_colors, daemons
645
- if daemon_id not in job_colors:
646
- d = Daemon(daemon_id=daemon_id)
647
- if d not in daemons:
648
- daemons = get_filtered_daemons(action)
649
- job_colors = build_job_colors(daemons)
650
- return job_colors[daemon_id]
651
-
652
- def follow_pretty_print():
653
- watchfiles = attempt_import('watchfiles')
654
- rich = import_rich()
655
- rich_text = attempt_import('rich.text')
656
- watch_daemon_ids = {d.daemon_id: d for d in daemons}
657
- info("Watching log files...")
658
-
659
- previous_line_timestamp = None
660
- def print_job_line(daemon, line):
661
- nonlocal previous_line_timestamp
662
- date_prefix_str = line[:len(now_str)]
663
- try:
664
- line_timestamp = datetime.strptime(date_prefix_str, timestamp_format)
665
- previous_line_timestamp = line_timestamp
666
- except Exception as e:
667
- line_timestamp = None
668
- if line_timestamp:
669
- line = line[(len(now_str) + 3):]
670
- else:
671
- line_timestamp = previous_line_timestamp
672
-
673
- if len(line) == 0 or line == '\n':
674
- return
675
-
676
- text = rich_text.Text(daemon.daemon_id)
677
- line_prefix = (
678
- get_buffer_spaces(daemon.daemon_id)
679
- + (line_timestamp.strftime(follow_timestamp_format) if line_timestamp else '')
680
- + ' | '
681
- )
682
- text.append(line_prefix + (line[:-1] if line[-1] == '\n' else line))
683
- if ANSI:
684
- text.stylize(
685
- get_job_colors(daemon.daemon_id),
686
- 0,
687
- len(daemon.daemon_id) + len(line_prefix)
688
- )
689
- get_console().print(text)
690
-
691
-
692
- def print_log_lines(daemon):
693
- for line in daemon.readlines():
694
- print_job_line(daemon, line)
695
-
696
- def seek_back_offset(d) -> bool:
697
- if d.log_offset_path.exists():
698
- d.log_offset_path.unlink()
699
-
700
- latest_subfile_path = d.rotating_log.get_latest_subfile_path()
701
- latest_subfile_index = d.rotating_log.get_latest_subfile_index()
702
-
703
- ### Sometimes the latest file is empty.
704
- if os.stat(latest_subfile_path).st_size == 0 and latest_subfile_index > 0:
705
- latest_subfile_index -= 1
706
- latest_subfile_path = d.rotating_log.get_subfile_path_from_index(
707
- latest_subfile_index
708
- )
709
-
710
- with open(latest_subfile_path, 'r', encoding='utf-8') as f:
711
- latest_lines = f.readlines()
638
+ def build_job_colors(_jobs, _old_job_colors=None) -> Dict[str, str]:
639
+ return {name: colors[i % len(colors)] for i, name in enumerate(_jobs)}
640
+
641
+ buffer_spaces = build_buffer_spaces(jobs)
642
+ job_colors = build_job_colors(jobs)
643
+
644
+ def get_buffer_spaces(name):
645
+ nonlocal buffer_spaces, jobs
646
+ if name not in buffer_spaces:
647
+ if name not in jobs:
648
+ jobs = get_filtered_jobs(executor_keys, action)
649
+ buffer_spaces = build_buffer_spaces(jobs)
650
+ return buffer_spaces[name] or ' '
651
+
652
+ def get_job_colors(name):
653
+ nonlocal job_colors, jobs
654
+ if name not in job_colors:
655
+ if name not in jobs:
656
+ jobs = get_filtered_jobs(executor_keys, action)
657
+ job_colors = build_job_colors(jobs)
658
+ return job_colors[name]
659
+
660
+ previous_line_timestamp = None
661
+ def print_job_line(job, line):
662
+ nonlocal previous_line_timestamp
663
+ date_prefix_str = line[:len(now_str)]
664
+ try:
665
+ line_timestamp = datetime.strptime(date_prefix_str, timestamp_format)
666
+ previous_line_timestamp = line_timestamp
667
+ except Exception as e:
668
+ line_timestamp = None
669
+ if line_timestamp:
670
+ line = line[(len(now_str) + 3):]
671
+ else:
672
+ line_timestamp = previous_line_timestamp
712
673
 
713
- lines_to_show = get_config('jobs', 'logs', 'lines_to_show')
714
- positions_to_rewind = len(''.join(latest_lines[(-1 * lines_to_show):]))
715
- backup_index = len(''.join(latest_lines)) - positions_to_rewind
674
+ if len(line) == 0 or line == '\n':
675
+ return
716
676
 
717
- d.rotating_log._cursor = (
718
- latest_subfile_index,
719
- max(0, backup_index)
677
+ text = rich_text.Text(job.name)
678
+ line_prefix = (
679
+ get_buffer_spaces(job.name)
680
+ + (line_timestamp.strftime(follow_timestamp_format) if line_timestamp else '')
681
+ + ' | '
682
+ )
683
+ text.append(line_prefix + (line[:-1] if line[-1] == '\n' else line))
684
+ if ANSI:
685
+ text.stylize(
686
+ get_job_colors(job.name),
687
+ 0,
688
+ len(job.name) + len(line_prefix)
720
689
  )
721
- d._write_log_offset()
722
- return True
690
+ get_console().print(text)
691
+
692
+ stop_event = asyncio.Event()
693
+ job_tasks = {}
694
+ job_stop_events = {}
695
+
696
+ async def refresh_job_tasks():
697
+ nonlocal job_tasks, jobs
698
+ while not stop_event.is_set():
699
+ jobs = get_filtered_jobs(executor_keys, action)
700
+ for name, job in jobs.items():
701
+ if name not in job_tasks:
702
+ job_stop_events[name] = asyncio.Event()
703
+ job_tasks[name] = asyncio.create_task(
704
+ job.monitor_logs_async(
705
+ partial(print_job_line, job),
706
+ stop_event=job_stop_events[name],
707
+ accept_input=False,
708
+ stop_on_exit=False,
709
+ )
710
+ )
711
+
712
+ for name, task in [(k, v) for k, v in job_tasks.items()]:
713
+ if name not in jobs:
714
+ job_stop_events[name].set()
715
+ task.cancel()
723
716
 
724
- daemons_being_watched = {
725
- d.daemon_id: d
726
- for d in daemons
727
- }
728
- for d in daemons:
729
- seek_back_offset(d)
730
- print_log_lines(d)
731
-
732
- _quit = False
733
- def watch_logs():
734
- for changes in watchfiles.watch(LOGS_RESOURCES_PATH):
735
- if _quit:
736
- return
737
- for change in changes:
738
- file_path_str = change[1]
739
- if '.log' not in file_path_str or '.log.offset' in file_path_str:
740
- continue
741
- file_path = pathlib.Path(file_path_str)
742
- if not file_path.exists():
743
- continue
744
- daemon_id = file_path.name.split('.log')[0]
745
- if daemon_id not in watch_daemon_ids and action:
746
- continue
747
717
  try:
748
- daemon = Daemon(daemon_id=daemon_id)
749
- except Exception as e:
750
- daemon = None
751
- warn(f"Seeing new logs for non-existent job '{daemon_id}'.", stack=False)
752
-
753
- if daemon is not None:
754
- print_log_lines(daemon)
755
-
756
- try:
757
- watch_logs()
758
- except KeyboardInterrupt as ki:
759
- _quit = True
718
+ await task
719
+ except asyncio.CancelledError:
720
+ pass
721
+ finally:
722
+ _ = job_tasks.pop(name, None)
723
+ _ = job_stop_events.pop(name, None)
760
724
 
761
- def print_nopretty_log_text():
762
- for d in daemons:
763
- log_text = d.log_text
764
- print(d.daemon_id)
765
- print(log_text)
725
+ await asyncio.sleep(1)
766
726
 
767
- print_log_text = follow_pretty_print if not nopretty else print_nopretty_log_text
768
- print_log_text()
727
+ async def gather_tasks():
728
+ tasks = [refresh_job_tasks()] + list(job_tasks.values())
729
+ await asyncio.gather(*tasks)
769
730
 
731
+ if not nopretty:
732
+ info("Watching logs...")
733
+ try:
734
+ asyncio.run(gather_tasks())
735
+ except KeyboardInterrupt:
736
+ pass
737
+ else:
738
+ for name, job in jobs.items():
739
+ print(f'\n-*-\nMRSM_JOB: {name}\n-*-')
740
+ print(job.get_logs())
770
741
  return True, "Success"
771
742
 
772
743
 
@@ -78,6 +78,9 @@ def _start_api(action: Optional[List[str]] = None, **kw):
78
78
  def _start_jobs(
79
79
  action: Optional[List[str]] = None,
80
80
  name: Optional[str] = None,
81
+ sysargs: Optional[List[str]] = None,
82
+ executor_keys: Optional[str] = None,
83
+ debug: bool = False,
81
84
  **kw
82
85
  ) -> SuccessTuple:
83
86
  """
@@ -111,11 +114,16 @@ def _start_jobs(
111
114
  """
112
115
  import textwrap
113
116
  from meerschaum.utils.warnings import warn, info
114
- from meerschaum.utils.daemon import (
115
- daemon_action, Daemon, get_daemon_ids, get_daemons, get_filtered_daemons,
116
- get_stopped_daemons, get_running_daemons, get_paused_daemons,
117
- )
118
117
  from meerschaum.utils.daemon._names import get_new_daemon_name
118
+ from meerschaum.utils.jobs import (
119
+ Job,
120
+ get_jobs,
121
+ get_filtered_jobs,
122
+ get_stopped_jobs,
123
+ get_running_jobs,
124
+ get_paused_jobs,
125
+ get_restart_jobs,
126
+ )
119
127
  from meerschaum._internal.arguments._parse_arguments import parse_arguments
120
128
  from meerschaum.actions import actions
121
129
  from meerschaum.utils.prompt import yes_no
@@ -125,7 +133,7 @@ def _start_jobs(
125
133
  from meerschaum.utils.misc import items_str
126
134
 
127
135
  names = []
128
- daemon_ids = get_daemon_ids()
136
+ jobs = get_filtered_jobs(executor_keys, debug=debug)
129
137
 
130
138
  new_job = len(list(action)) > 0
131
139
  _potential_jobs = {'known': [], 'unknown': []}
@@ -134,7 +142,7 @@ def _start_jobs(
134
142
  for a in action:
135
143
  _potential_jobs[(
136
144
  'known'
137
- if a in daemon_ids
145
+ if a in jobs
138
146
  else 'unknown'
139
147
  )].append(a)
140
148
 
@@ -182,88 +190,83 @@ def _start_jobs(
182
190
 
183
191
  ### No action or --name was provided. Ask to start all stopped jobs.
184
192
  else:
185
- _running_daemons = get_running_daemons()
186
- _paused_daemons = get_paused_daemons()
187
- _stopped_daemons = get_stopped_daemons()
188
- if not _stopped_daemons and not _paused_daemons:
189
- if not _running_daemons:
190
- return False, "No jobs to start."
193
+ running_jobs = get_running_jobs(executor_keys, jobs, debug=debug)
194
+ paused_jobs = get_paused_jobs(executor_keys, jobs, debug=debug)
195
+ stopped_jobs = get_stopped_jobs(executor_keys, jobs, debug=debug)
196
+
197
+ if not stopped_jobs and not paused_jobs:
198
+ if not running_jobs:
199
+ return False, "No jobs to start"
191
200
  return True, "All jobs are running."
192
201
 
193
- names = [d.daemon_id for d in _stopped_daemons + _paused_daemons]
202
+ names = [
203
+ name
204
+ for name in list(stopped_jobs) + list(paused_jobs)
205
+ ]
194
206
 
195
207
  def _run_new_job(name: Optional[str] = None):
196
- kw['action'] = action
197
208
  name = name or get_new_daemon_name()
198
- kw['name'] = name
199
- _action_success_tuple = daemon_action(daemon_id=name, **kw)
200
- return _action_success_tuple, name
201
-
202
- def _run_existing_job(name: Optional[str] = None):
203
- daemon = Daemon(daemon_id=name)
204
- if daemon.process is not None:
205
- if daemon.status == 'paused':
206
- return daemon.resume(), daemon.daemon_id
207
- return (True, f"Job '{name}' is already running."), daemon.daemon_id
208
-
209
- if not daemon.path.exists():
210
- if not kw.get('nopretty', False):
211
- warn(f"There isn't a job with the name '{name}'.", stack=False)
212
- print(
213
- f"You can start a new job named '{name}' with `start job "
214
- + "{options}" + f" --name {name}`"
215
- )
216
- return (False, f"Job '{name}' does not exist."), daemon.daemon_id
209
+ job = Job(name, sysargs, executor_keys=executor_keys)
210
+ return job.start(debug=debug), name
217
211
 
218
- return daemon.run(allow_dirty_run=True), daemon.daemon_id
212
+ def _run_existing_job(name: str):
213
+ job = Job(name, executor_keys=executor_keys)
214
+ return job.start(debug=debug), name
219
215
 
220
216
  if not names:
221
217
  return False, "No jobs to start."
222
218
 
223
219
  ### Get user permission to clear logs.
224
- _filtered_daemons = get_filtered_daemons(names)
225
- if not kw.get('force', False) and _filtered_daemons:
226
- _filtered_running_daemons = get_running_daemons(_filtered_daemons)
227
- _skipped_daemons = []
228
- if _filtered_running_daemons:
229
- pprint_jobs(_filtered_running_daemons)
220
+ _filtered_jobs = get_filtered_jobs(executor_keys, names, debug=debug)
221
+ if not kw.get('force', False) and _filtered_jobs:
222
+ _filtered_running_jobs = get_running_jobs(executor_keys, _filtered_jobs, debug=debug)
223
+ _skipped_jobs = []
224
+ if _filtered_running_jobs:
225
+ pprint_jobs(_filtered_running_jobs)
230
226
  if yes_no(
231
227
  "Do you want to first stop these jobs?",
232
- default = 'n',
233
- yes = kw.get('yes', False),
234
- noask = kw.get('noask', False)
228
+ default='n',
229
+ yes=kw.get('yes', False),
230
+ noask=kw.get('noask', False)
235
231
  ):
236
232
  stop_success_tuple = actions['stop'](
237
- action = ['jobs'] + [d.daemon_id for d in _filtered_running_daemons],
238
- force = True,
233
+ action=['jobs'] + [_name for _name in _filtered_running_jobs],
234
+ force=True,
235
+ mrsm_instance=mrsm_instance,
236
+ debug=debug,
239
237
  )
240
238
  if not stop_success_tuple[0]:
241
239
  warn(
242
- "Failed to stop job" + ("s" if len(_filtered_running_daemons) != 1 else '')
243
- + items_str([d.daemon_id for d in _filtered_running_daemons])
244
- + ".",
245
- stack = False
240
+ (
241
+ "Failed to stop job"
242
+ + ("s" if len(_filtered_running_jobs) != 1 else '')
243
+ + items_str([_name for _name in _filtered_running_jobs])
244
+ + "."
245
+ ),
246
+ stack=False
246
247
  )
247
- for d in _filtered_running_daemons:
248
- names.remove(d.daemon_id)
249
- _filtered_daemons.remove(d)
248
+ for _name in _filtered_running_jobs:
249
+ names.remove(_name)
250
+ _filtered_jobs.pop(_name)
250
251
  else:
251
252
  info(
252
253
  "Skipping already running job"
253
- + ("s" if len(_filtered_running_daemons) != 1 else '') + ' '
254
- + items_str([d.daemon_id for d in _filtered_running_daemons]) + '.'
254
+ + ("s" if len(_filtered_running_jobs) != 1 else '')
255
+ + ' '
256
+ + items_str([_name for _name in _filtered_running_jobs])
257
+ + '.'
255
258
  )
256
- for d in _filtered_running_daemons:
257
- names.remove(d.daemon_id)
258
- _filtered_daemons.remove(d)
259
- _skipped_daemons.append(d)
259
+ for _name in _filtered_running_jobs:
260
+ names.remove(_name)
261
+ _filtered_jobs.pop(_name)
262
+ _skipped_jobs.append(_name)
260
263
 
261
- if not _filtered_daemons:
262
- return len(_skipped_daemons) > 0, "No jobs to start."
264
+ if not _filtered_jobs:
265
+ return len(_skipped_jobs) > 0, "No jobs to start."
263
266
 
264
- pprint_jobs(_filtered_daemons, nopretty=kw.get('nopretty', False))
267
+ pprint_jobs(_filtered_jobs, nopretty=kw.get('nopretty', False))
265
268
  info(
266
- f"Starting the job"
269
+ "Starting the job"
267
270
  + ("s" if len(names) != 1 else "")
268
271
  + " " + items_str(names)
269
272
  + "..."
@@ -278,7 +281,11 @@ def _start_jobs(
278
281
  )
279
282
  if not kw.get('nopretty', False):
280
283
  print_tuple(success_tuple)
281
- _successes.append(_name) if success_tuple[0] else _failures.append(_name)
284
+
285
+ if success_tuple[0]:
286
+ _successes.append(_name)
287
+ else:
288
+ _failures.append(_name)
282
289
 
283
290
  msg = (
284
291
  (("Successfully started job" + ("s" if len(_successes) != 1 else '')
@@ -289,25 +296,28 @@ def _start_jobs(
289
296
  )
290
297
  return len(_failures) == 0, msg
291
298
 
299
+
292
300
  def _complete_start_jobs(
293
- action: Optional[List[str]] = None,
294
- line: str = '',
295
- **kw
296
- ) -> List[str]:
297
- from meerschaum.utils.daemon import get_daemon_ids
298
- daemon_ids = get_daemon_ids()
301
+ action: Optional[List[str]] = None,
302
+ executor_keys: Optional[str] = None,
303
+ line: str = '',
304
+ **kw
305
+ ) -> List[str]:
306
+ from meerschaum.utils.jobs import get_filtered_jobs
307
+ jobs = get_filtered_jobs(executor_keys, action)
299
308
  if not action:
300
- return daemon_ids
309
+ return list(jobs)
310
+
301
311
  possibilities = []
302
312
  _line_end = line.split(' ')[-1]
303
- for daemon_id in daemon_ids:
304
- if daemon_id in action:
313
+ for name in jobs:
314
+ if name in action:
305
315
  continue
306
316
  if _line_end == '':
307
- possibilities.append(daemon_id)
317
+ possibilities.append(name)
308
318
  continue
309
- if daemon_id.startswith(action[-1]):
310
- possibilities.append(daemon_id)
319
+ if name.startswith(action[-1]):
320
+ possibilities.append(name)
311
321
  return possibilities
312
322
 
313
323