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
@@ -9,6 +9,7 @@ Start subsystems (API server, logging daemon, etc.).
9
9
  from __future__ import annotations
10
10
  from meerschaum.utils.typing import SuccessTuple, Optional, List, Any, Union, Dict
11
11
 
12
+
12
13
  def start(
13
14
  action: Optional[List[str]] = None,
14
15
  **kw: Any,
@@ -375,8 +376,8 @@ def _start_gui(
375
376
  webview.create_window(
376
377
  'Meerschaum Shell',
377
378
  f'http://127.0.0.1:{port}',
378
- height = 650,
379
- width = 1000
379
+ height=650,
380
+ width=1000
380
381
  )
381
382
  webview.start(debug=debug)
382
383
  except Exception as e:
@@ -540,6 +541,7 @@ def _start_pipeline(
540
541
  action: Optional[List[str]] = None,
541
542
  loop: bool = False,
542
543
  min_seconds: Union[float, int, None] = 1.0,
544
+ timeout_seconds: Optional[int] = None,
543
545
  params: Optional[Dict[str, Any]] = None,
544
546
  **kwargs
545
547
  ) -> SuccessTuple:
@@ -556,10 +558,39 @@ def _start_pipeline(
556
558
  `show version + show arguments :: --loop`
557
559
 
558
560
  """
561
+ import json
559
562
  import time
563
+ import sys
560
564
  from meerschaum._internal.entry import entry
561
565
  from meerschaum.utils.warnings import info, warn
562
566
  from meerschaum.utils.misc import is_int
567
+ from meerschaum.utils.venv import venv_exec
568
+ from meerschaum.utils.process import poll_process
569
+ fence_begin, fence_end = '<MRSM_RESULT>', '</MRSM_RESULT>'
570
+
571
+ success, msg = False, "Did not run pipeline."
572
+ def write_line(line):
573
+ nonlocal success, msg
574
+ decoded = line.decode('utf-8')
575
+ begin_index, end_index = decoded.find(fence_begin), decoded.find(fence_end)
576
+
577
+ ### Found the beginning of the return value.
578
+ ### Don't write the parsed success tuple message.
579
+ if begin_index >= 0:
580
+ success, msg = tuple(json.loads(
581
+ decoded[begin_index + len(fence_begin):end_index]
582
+ ))
583
+ return
584
+
585
+ print(decoded)
586
+
587
+ def timeout_handler(*args, **kw):
588
+ nonlocal success, msg
589
+ success, msg = False, (
590
+ f"Failed to execute pipeline within {timeout_seconds} second"
591
+ + ('s' if timeout_seconds != 1 else '') + '.'
592
+ )
593
+ write_line((fence_begin + json.dumps((success, msg)) + fence_end).encode('utf-8'))
563
594
 
564
595
  do_n_times = (
565
596
  int(action[0].lstrip('x'))
@@ -577,12 +608,38 @@ def _start_pipeline(
577
608
  if min_seconds is None:
578
609
  min_seconds = 1.0
579
610
 
611
+ def do_entry() -> None:
612
+ nonlocal success, msg
613
+ if timeout_seconds is None:
614
+ success, msg = entry(sub_args_line, _patch_args=patch_args)
615
+ return
616
+
617
+ sub_args_line_escaped = sub_args_line.replace("'", "<QUOTE>")
618
+ patch_args_escaped_str = json.dumps(patch_args).replace("'", "<QUOTE>")
619
+ src = (
620
+ "import json\n"
621
+ "from meerschaum._internal.entry import entry\n\n"
622
+ f"sub_args_line = '{sub_args_line_escaped}'.replace(\"<QUOTE>\", \"'\")\n"
623
+ f"patch_args = json.loads('{patch_args_escaped_str}'.replace('<QUOTE>', \"'\"))\n"
624
+ "success, msg = entry(sub_args_line, _patch_args=patch_args)\n"
625
+ f"print('{fence_begin}' + json.dumps((success, msg)) + '{fence_end}')"
626
+ )
627
+ proc = venv_exec(src, venv=None, as_proc=True)
628
+ poll_process(
629
+ proc,
630
+ write_line,
631
+ timeout_seconds,
632
+ timeout_handler,
633
+ )
634
+
580
635
  ran_n_times = 0
581
- success, msg = False, "Did not run pipeline."
582
636
  def run_loop():
583
637
  nonlocal ran_n_times, success, msg
584
638
  while True:
585
- success, msg = entry(sub_args_line, _patch_args=patch_args)
639
+ try:
640
+ do_entry()
641
+ except Exception as e:
642
+ warn(e)
586
643
  ran_n_times += 1
587
644
 
588
645
  if not loop and do_n_times == 1:
@@ -129,7 +129,6 @@ def _pipes_lap(
129
129
  stack=False,
130
130
  )
131
131
 
132
-
133
132
  def _task_label(count: int):
134
133
  return f"[cyan]Syncing {count} pipe{'s' if count != 1 else ''}..."
135
134
 
@@ -137,7 +136,6 @@ def _pipes_lap(
137
136
  _progress.add_task(_task_label(len(pipes)), start=True, total=len(pipes))
138
137
  ) if _progress is not None else None
139
138
 
140
-
141
139
  def worker_fn():
142
140
  while not stop_event.is_set():
143
141
  try:
@@ -188,7 +186,7 @@ def _pipes_lap(
188
186
  decoded[begin_index + len(fence_begin):end_index]
189
187
  ))
190
188
  return
191
- sys.stdout.buffer.write(line)
189
+ print(decoded)
192
190
 
193
191
  def timeout_handler(p, *args, **kw):
194
192
  success, msg = False, (
@@ -227,7 +225,7 @@ def _pipes_lap(
227
225
  write_line,
228
226
  timeout_seconds,
229
227
  timeout_handler,
230
- (p,)
228
+ (p,),
231
229
  )
232
230
  return _success_tuple
233
231
 
@@ -249,18 +247,18 @@ def _pipes_lap(
249
247
 
250
248
 
251
249
  def _sync_pipes(
252
- loop: bool = False,
253
- min_seconds: int = 1,
254
- unblock: bool = False,
255
- verify: bool = False,
256
- deduplicate: bool = False,
257
- bounded: Optional[bool] = None,
258
- chunk_interval: Union[timedelta, int, None] = None,
259
- shell: bool = False,
260
- nopretty: bool = False,
261
- debug: bool = False,
262
- **kw: Any
263
- ) -> SuccessTuple:
250
+ loop: bool = False,
251
+ min_seconds: int = 1,
252
+ unblock: bool = False,
253
+ verify: bool = False,
254
+ deduplicate: bool = False,
255
+ bounded: Optional[bool] = None,
256
+ chunk_interval: Union[timedelta, int, None] = None,
257
+ shell: bool = False,
258
+ nopretty: bool = False,
259
+ debug: bool = False,
260
+ **kw: Any
261
+ ) -> SuccessTuple:
264
262
  """
265
263
  Fetch and sync new data for pipes.
266
264
 
@@ -9,16 +9,17 @@ Upgrade your current Meerschaum environment
9
9
  from __future__ import annotations
10
10
  from meerschaum.utils.typing import SuccessTuple, Any, List, Optional, Union
11
11
 
12
+
12
13
  def upgrade(
13
14
  action: Optional[List[str]] = None,
14
15
  **kw: Any
15
16
  ) -> SuccessTuple:
16
17
  """
17
18
  Upgrade Meerschaum, plugins, or packages.
18
-
19
+
19
20
  Command:
20
21
  `upgrade {option}`
21
-
22
+
22
23
  Example:
23
24
  `upgrade meerschaum`
24
25
  """
@@ -77,7 +78,7 @@ def _upgrade_meerschaum(
77
78
  if force:
78
79
  answer = True
79
80
  else:
80
- answer = yes_no(f"Take down the stack?", default='y', yes=yes, noask=noask)
81
+ answer = yes_no("Take down the stack?", default='n', yes=yes, noask=noask)
81
82
 
82
83
  if answer:
83
84
  if debug:
@@ -95,7 +96,7 @@ def _upgrade_meerschaum(
95
96
  if debug:
96
97
  dprint('Upgrade meerschaum with dependencies: \"' + f'{dependencies}' + '\"')
97
98
  if not pip_install(install_name, venv=None, debug=debug):
98
- return False, f"Failed to upgrade Meerschaum via pip."
99
+ return False, "Failed to upgrade Meerschaum via pip."
99
100
 
100
101
  if debug:
101
102
  dprint("Pulling new Docker images...")
@@ -98,6 +98,7 @@ _paths = {
98
98
  'plugins' : pages.plugins.layout,
99
99
  'register': pages.register.layout,
100
100
  'pipes' : pages.pipes.layout,
101
+ 'job' : pages.job.layout,
101
102
  }
102
103
  _required_login = {''}
103
104
 
@@ -121,7 +122,7 @@ def update_page_layout_div(
121
122
  ----------
122
123
  pathname: str
123
124
  The path in the browser.
124
-
125
+
125
126
  session_store_data: Dict[str, Any]:
126
127
  The stored session data.
127
128
 
@@ -12,6 +12,7 @@ import json
12
12
  import time
13
13
  import traceback
14
14
  from datetime import datetime, timezone
15
+
15
16
  from meerschaum.utils.typing import Optional, Dict, Any
16
17
  from meerschaum.api import CHECK_UPDATE
17
18
  from meerschaum.api.dash import dash_app
@@ -19,17 +20,19 @@ from meerschaum.api.dash.sessions import get_username_from_session
19
20
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html
20
21
  from meerschaum.api.dash.components import alert_from_success_tuple
21
22
  from meerschaum.api.dash.jobs import (
23
+ build_job_card,
22
24
  build_manage_job_buttons_div_children,
23
25
  build_status_children,
24
26
  build_process_timestamps_children,
25
27
  )
26
- from meerschaum.jobs import Job
28
+ from meerschaum.api.routes._jobs import _get_job
27
29
  from meerschaum.api.dash.sessions import is_session_authenticated
28
30
  dash = attempt_import('dash', lazy=False, check_update=CHECK_UPDATE)
29
31
  html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
30
32
  from dash.exceptions import PreventUpdate
31
33
  from dash.dependencies import Input, Output, State, ALL, MATCH
32
34
  import dash_bootstrap_components as dbc
35
+ from dash import no_update
33
36
 
34
37
 
35
38
  @dash_app.callback(
@@ -53,6 +56,13 @@ def download_job_logs(n_clicks):
53
56
 
54
57
  component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
55
58
  job_name = component_dict['index']
59
+ try:
60
+ job = _get_job(job_name)
61
+ except Exception:
62
+ job = None
63
+ if job is None or not job.exists():
64
+ raise PreventUpdate
65
+
56
66
  now = datetime.now(timezone.utc)
57
67
  filename = job_name + '_' + str(int(now.timestamp())) + '.log'
58
68
  return {
@@ -69,7 +79,7 @@ def download_job_logs(n_clicks):
69
79
  Input({'type': 'manage-job-button', 'action': ALL, 'index': MATCH}, 'n_clicks'),
70
80
  State('session-store', 'data'),
71
81
  State({'type': 'job-label-p', 'index': MATCH}, 'children'),
72
- prevent_initial_call = True,
82
+ prevent_initial_call=True,
73
83
  )
74
84
  def manage_job_button_click(
75
85
  n_clicks: Optional[int] = None,
@@ -102,10 +112,10 @@ def manage_job_button_click(
102
112
  job_name = component_dict['index']
103
113
  manage_job_action = component_dict['action']
104
114
  try:
105
- job = Job(job_name, job_label.replace('\n', ' ') if job_label else None)
106
- except Exception as e:
115
+ job = _get_job(job_name, job_label.replace('\n', ' ') if job_label else None)
116
+ except Exception:
107
117
  job = None
108
- if job is None:
118
+ if job is None or not job.exists():
109
119
  raise PreventUpdate
110
120
 
111
121
  manage_functions = {
@@ -191,7 +201,7 @@ dash_app.clientside_callback(
191
201
  Output({'type': 'process-timestamps-div', 'index': ALL}, 'children'),
192
202
  Input('refresh-jobs-interval', 'n_intervals'),
193
203
  State('session-store', 'data'),
194
- prevent_initial_call = True,
204
+ prevent_initial_call=True,
195
205
  )
196
206
  def refresh_jobs_on_interval(
197
207
  n_intervals: Optional[int] = None,
@@ -209,7 +219,7 @@ def refresh_jobs_on_interval(
209
219
  ]
210
220
 
211
221
  ### NOTE: The job may have been deleted, but the card may still exist.
212
- jobs = [Job(name) for name in job_names]
222
+ jobs = [_get_job(name) for name in job_names]
213
223
 
214
224
  return (
215
225
  [
@@ -229,3 +239,39 @@ def refresh_jobs_on_interval(
229
239
  for job in jobs
230
240
  ],
231
241
  )
242
+
243
+
244
+ @dash_app.callback(
245
+ Output('job-output-div', 'children'),
246
+ Input('job-location', 'pathname'),
247
+ State('session-store', 'data'),
248
+ )
249
+ def render_job_page_from_url(
250
+ pathname: str,
251
+ session_data: Optional[Dict[str, Any]],
252
+ ):
253
+ """
254
+ Load the `/job/{name}` page.
255
+ """
256
+ if not str(pathname).startswith('/dash/job'):
257
+ return no_update
258
+
259
+ session_id = (session_data or {}).get('session-id', None)
260
+ authenticated = is_session_authenticated(str(session_id))
261
+
262
+ job_name = pathname.replace('/dash/job', '').lstrip('/').rstrip('/')
263
+ if not job_name:
264
+ return no_update
265
+
266
+ job = _get_job(job_name)
267
+ if not job.exists():
268
+ return [
269
+ html.Br(),
270
+ html.H2("404: Job does not exist."),
271
+ ]
272
+
273
+ return [
274
+ html.Br(),
275
+ build_job_card(job, authenticated=authenticated, include_follow=False),
276
+ html.Br(),
277
+ ]
@@ -26,7 +26,7 @@ html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHEC
26
26
  State('pipes-location', 'search'),
27
27
  State('session-store', 'data'),
28
28
  )
29
- def render_page_from_url(
29
+ def render_pipe_page_from_url(
30
30
  pathname: str,
31
31
  pipe_search: str,
32
32
  session_data: Optional[Dict[str, Any]],
@@ -31,6 +31,7 @@ STATUS_EMOJI: Dict[str, str] = {
31
31
 
32
32
  EXECUTOR_KEYS: str = get_executor_keys_from_context()
33
33
 
34
+
34
35
  def get_jobs_cards(state: WebState):
35
36
  """
36
37
  Build cards and alerts lists for jobs.
@@ -42,71 +43,96 @@ def get_jobs_cards(state: WebState):
42
43
  cards = []
43
44
 
44
45
  for name, job in jobs.items():
45
- footer_children = html.Div(
46
- build_process_timestamps_children(job),
47
- id = {'type': 'process-timestamps-div', 'index': name},
48
- )
49
- follow_logs_button = dbc.DropdownMenuItem(
50
- "Follow logs",
51
- id = {'type': 'follow-logs-button', 'index': name},
52
- )
53
- download_logs_button = dbc.DropdownMenuItem(
54
- "Download logs",
55
- id = {'type': 'job-download-logs-button', 'index': name},
56
- )
57
- logs_menu_children = (
58
- ([follow_logs_button] if is_authenticated else []) + [download_logs_button]
59
- )
60
- header_children = [
61
- html.Div(
62
- build_status_children(job),
63
- id={'type': 'manage-job-status-div', 'index': name},
64
- style={'float': 'left'},
46
+ cards.append(build_job_card(job, authenticated=is_authenticated))
47
+
48
+ return cards, []
49
+
50
+
51
+ def build_job_card(
52
+ job: Job,
53
+ authenticated: bool = False,
54
+ include_follow: bool = True,
55
+ ):
56
+ """
57
+ Return a card for a given job.
58
+ """
59
+ footer_children = html.Div(
60
+ build_process_timestamps_children(job),
61
+ id={'type': 'process-timestamps-div', 'index': job.name},
62
+ )
63
+ follow_logs_button = dbc.DropdownMenuItem(
64
+ "Follow logs",
65
+ id={'type': 'follow-logs-button', 'index': job.name},
66
+ )
67
+ download_logs_button = dbc.DropdownMenuItem(
68
+ "Download logs",
69
+ id={'type': 'job-download-logs-button', 'index': job.name},
70
+ )
71
+ logs_menu_children = (
72
+ ([follow_logs_button] if include_follow else []) + [download_logs_button]
73
+ if authenticated
74
+ else []
75
+ )
76
+ header_children = [
77
+ html.Div(
78
+ build_status_children(job),
79
+ id={'type': 'manage-job-status-div', 'index': job.name},
80
+ style={'float': 'left'},
81
+ ),
82
+ ] + ([
83
+ html.Div(
84
+ dbc.DropdownMenu(
85
+ logs_menu_children,
86
+ label="Logs",
87
+ size="sm",
88
+ align_end=True,
89
+ color="secondary",
90
+ menu_variant='dark',
65
91
  ),
66
- html.Div(
67
- dbc.DropdownMenu(
68
- logs_menu_children,
69
- label="Logs",
70
- size="sm",
71
- align_end=True,
72
- color="secondary",
73
- menu_variant='dark',
74
- ),
75
- style={'float': 'right'},
92
+ style={'float': 'right'},
93
+ ),
94
+ ] if authenticated else [])
95
+
96
+ body_children = [
97
+ html.H4(
98
+ html.B(
99
+ html.A(
100
+ "🔗 " + job.name,
101
+ href=f"/dash/job/{job.name}",
102
+ target="_blank",
103
+ style={
104
+ 'color': 'white',
105
+ 'text-decoration': 'none',
106
+ },
107
+ )
76
108
  ),
77
- ]
78
-
79
- body_children = [
80
- html.H4(html.B(name), className="card-title"),
81
- html.Div(
82
- html.P(
83
- job.label,
84
- className="card-text job-card-text",
85
- style={"word-wrap": "break-word"},
86
- id={'type': 'job-label-p', 'index': name},
87
- ),
88
- style={"white-space": "pre-wrap"},
109
+ className="card-title",
110
+ ),
111
+ html.Div(
112
+ html.P(
113
+ job.label,
114
+ className="card-text job-card-text",
115
+ style={"word-wrap": "break-word"},
116
+ id={'type': 'job-label-p', 'index': job.name},
89
117
  ),
90
- html.Div(
91
- (
92
- build_manage_job_buttons_div_children(job)
93
- if is_authenticated
94
- else []
95
- ),
96
- id={'type': 'manage-job-buttons-div', 'index': name},
118
+ style={"white-space": "pre-wrap"},
119
+ ),
120
+ html.Div(
121
+ (
122
+ build_manage_job_buttons_div_children(job)
123
+ if authenticated
124
+ else []
97
125
  ),
98
- html.Div(id={'type': 'manage-job-alert-div', 'index': name}),
99
- ]
100
-
101
- cards.append(
102
- dbc.Card([
103
- dbc.CardHeader(header_children),
104
- dbc.CardBody(body_children),
105
- dbc.CardFooter(footer_children),
106
- ])
107
- )
126
+ id={'type': 'manage-job-buttons-div', 'index': job.name},
127
+ ),
128
+ html.Div(id={'type': 'manage-job-alert-div', 'index': job.name}),
129
+ ]
108
130
 
109
- return cards, []
131
+ return dbc.Card([
132
+ dbc.CardHeader(header_children),
133
+ dbc.CardBody(body_children),
134
+ dbc.CardFooter(footer_children),
135
+ ])
110
136
 
111
137
 
112
138
  def build_manage_job_buttons_div_children(job: Job):
@@ -11,3 +11,4 @@ import meerschaum.api.dash.pages.dashboard
11
11
  import meerschaum.api.dash.pages.plugins
12
12
  import meerschaum.api.dash.pages.register
13
13
  import meerschaum.api.dash.pages.pipes
14
+ import meerschaum.api.dash.pages.job
@@ -0,0 +1,21 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Display pipes via a shareable URL.
6
+ """
7
+
8
+ from meerschaum.api import CHECK_UPDATE
9
+ from meerschaum.utils.packages import import_html, import_dcc
10
+
11
+ html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
12
+ import dash_bootstrap_components as dbc
13
+
14
+ from meerschaum.api.dash.components import download_logs, refresh_jobs_interval
15
+
16
+ layout = dbc.Container([
17
+ dcc.Location('job-location'),
18
+ html.Div(id='job-output-div'),
19
+ download_logs,
20
+ refresh_jobs_interval,
21
+ ])
@@ -39,12 +39,12 @@ NONINTERACTIVE_ENV: str = STATIC_CONFIG['environment']['noninteractive']
39
39
  EXECUTOR_KEYS: str = 'local'
40
40
 
41
41
 
42
- def _get_job(name: str):
43
- systemd_job = Job(name, executor_keys='systemd')
42
+ def _get_job(name: str, sysargs: Union[str, List[str], None] = None):
43
+ systemd_job = Job(name, sysargs, executor_keys='systemd')
44
44
  if systemd_job.exists():
45
45
  return systemd_job
46
46
 
47
- job = Job(name, executor_keys=EXECUTOR_KEYS)
47
+ job = Job(name, sysargs, executor_keys=EXECUTOR_KEYS)
48
48
  return job
49
49
 
50
50
 
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.4.11"
5
+ __version__ = "2.4.13"