meerschaum 2.2.6__py3-none-any.whl → 2.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.
- meerschaum/__init__.py +6 -1
- meerschaum/__main__.py +9 -9
- meerschaum/_internal/arguments/__init__.py +1 -1
- meerschaum/_internal/arguments/_parse_arguments.py +72 -6
- meerschaum/_internal/arguments/_parser.py +45 -15
- meerschaum/_internal/docs/index.py +265 -8
- meerschaum/_internal/entry.py +167 -37
- meerschaum/_internal/shell/Shell.py +290 -99
- meerschaum/_internal/shell/updates.py +175 -0
- meerschaum/actions/__init__.py +29 -17
- meerschaum/actions/api.py +12 -12
- meerschaum/actions/attach.py +113 -0
- meerschaum/actions/copy.py +68 -41
- meerschaum/actions/delete.py +112 -50
- meerschaum/actions/edit.py +3 -3
- meerschaum/actions/install.py +40 -32
- meerschaum/actions/pause.py +44 -27
- meerschaum/actions/register.py +19 -5
- meerschaum/actions/restart.py +107 -0
- meerschaum/actions/show.py +130 -159
- meerschaum/actions/start.py +161 -100
- meerschaum/actions/stop.py +78 -42
- meerschaum/actions/sync.py +3 -3
- meerschaum/actions/upgrade.py +28 -36
- meerschaum/api/_events.py +25 -1
- meerschaum/api/_oauth2.py +2 -0
- meerschaum/api/_websockets.py +2 -2
- meerschaum/api/dash/callbacks/jobs.py +36 -44
- meerschaum/api/dash/jobs.py +89 -78
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +148 -17
- meerschaum/api/routes/_jobs.py +407 -0
- meerschaum/api/routes/_pipes.py +25 -25
- meerschaum/config/_default.py +1 -0
- meerschaum/config/_formatting.py +1 -0
- meerschaum/config/_jobs.py +1 -1
- meerschaum/config/_paths.py +11 -0
- meerschaum/config/_shell.py +84 -67
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +18 -0
- meerschaum/connectors/Connector.py +13 -7
- meerschaum/connectors/__init__.py +28 -15
- meerschaum/connectors/api/APIConnector.py +27 -1
- meerschaum/connectors/api/_actions.py +71 -6
- meerschaum/connectors/api/_jobs.py +368 -0
- meerschaum/connectors/api/_misc.py +1 -1
- meerschaum/connectors/api/_pipes.py +85 -84
- meerschaum/connectors/api/_request.py +13 -9
- meerschaum/connectors/parse.py +27 -15
- meerschaum/core/Pipe/_bootstrap.py +16 -8
- meerschaum/core/Pipe/_sync.py +3 -0
- meerschaum/jobs/_Executor.py +69 -0
- meerschaum/jobs/_Job.py +899 -0
- meerschaum/jobs/__init__.py +396 -0
- meerschaum/jobs/systemd.py +694 -0
- meerschaum/plugins/__init__.py +97 -12
- meerschaum/utils/daemon/Daemon.py +352 -147
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +19 -10
- meerschaum/utils/daemon/RotatingFile.py +22 -8
- meerschaum/utils/daemon/StdinFile.py +121 -0
- meerschaum/utils/daemon/__init__.py +42 -27
- meerschaum/utils/daemon/_names.py +15 -13
- meerschaum/utils/formatting/__init__.py +83 -37
- meerschaum/utils/formatting/_jobs.py +146 -55
- meerschaum/utils/formatting/_shell.py +6 -0
- meerschaum/utils/misc.py +41 -22
- meerschaum/utils/packages/__init__.py +21 -15
- meerschaum/utils/packages/_packages.py +9 -6
- meerschaum/utils/process.py +9 -9
- meerschaum/utils/prompt.py +20 -7
- meerschaum/utils/schedule.py +21 -15
- meerschaum/utils/venv/__init__.py +2 -2
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/METADATA +22 -25
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/RECORD +80 -70
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.6.dist-info → meerschaum-2.3.0.dist-info}/zip-safe +0 -0
@@ -9,10 +9,12 @@ Intercept OS-level file descriptors.
|
|
9
9
|
import os
|
10
10
|
import select
|
11
11
|
import traceback
|
12
|
+
import errno
|
12
13
|
from threading import Event
|
13
14
|
from datetime import datetime
|
14
15
|
from meerschaum.utils.typing import Callable
|
15
16
|
from meerschaum.utils.warnings import warn
|
17
|
+
from meerschaum.config.paths import DAEMON_ERROR_LOG_PATH
|
16
18
|
|
17
19
|
FD_CLOSED: int = 9
|
18
20
|
STOP_READING_FD_EVENT: Event = Event()
|
@@ -65,8 +67,13 @@ class FileDescriptorInterceptor:
|
|
65
67
|
except BlockingIOError:
|
66
68
|
continue
|
67
69
|
except OSError as e:
|
68
|
-
|
69
|
-
|
70
|
+
if e.errno == errno.EBADF:
|
71
|
+
### File descriptor is closed.
|
72
|
+
pass
|
73
|
+
elif e.errno == errno.EINTR:
|
74
|
+
continue # Interrupted system call, just try again
|
75
|
+
else:
|
76
|
+
warn(f"OSError in FileDescriptorInterceptor: {e}")
|
70
77
|
break
|
71
78
|
|
72
79
|
try:
|
@@ -86,9 +93,11 @@ class FileDescriptorInterceptor:
|
|
86
93
|
else data.replace(b'\n', b'\n' + injected_bytes)
|
87
94
|
)
|
88
95
|
os.write(self.new_file_descriptor, modified_data)
|
89
|
-
except
|
90
|
-
|
91
|
-
|
96
|
+
except (BrokenPipeError, OSError):
|
97
|
+
break
|
98
|
+
except Exception:
|
99
|
+
with open(DAEMON_ERROR_LOG_PATH, 'a+', encoding='utf-8') as f:
|
100
|
+
f.write(traceback.format_exc())
|
92
101
|
break
|
93
102
|
|
94
103
|
|
@@ -103,7 +112,7 @@ class FileDescriptorInterceptor:
|
|
103
112
|
except OSError as e:
|
104
113
|
if e.errno != FD_CLOSED:
|
105
114
|
warn(
|
106
|
-
|
115
|
+
"Error while trying to close the duplicated file descriptor:\n"
|
107
116
|
+ f"{traceback.format_exc()}"
|
108
117
|
)
|
109
118
|
|
@@ -112,7 +121,7 @@ class FileDescriptorInterceptor:
|
|
112
121
|
except OSError as e:
|
113
122
|
if e.errno != FD_CLOSED:
|
114
123
|
warn(
|
115
|
-
|
124
|
+
"Error while trying to close the write-pipe "
|
116
125
|
+ "to the intercepted file descriptor:\n"
|
117
126
|
+ f"{traceback.format_exc()}"
|
118
127
|
)
|
@@ -121,7 +130,7 @@ class FileDescriptorInterceptor:
|
|
121
130
|
except OSError as e:
|
122
131
|
if e.errno != FD_CLOSED:
|
123
132
|
warn(
|
124
|
-
|
133
|
+
"Error while trying to close the read-pipe "
|
125
134
|
+ "to the intercepted file descriptor:\n"
|
126
135
|
+ f"{traceback.format_exc()}"
|
127
136
|
)
|
@@ -131,7 +140,7 @@ class FileDescriptorInterceptor:
|
|
131
140
|
except OSError as e:
|
132
141
|
if e.errno != FD_CLOSED:
|
133
142
|
warn(
|
134
|
-
|
143
|
+
"Error while trying to close the signal-read-pipe "
|
135
144
|
+ "to the intercepted file descriptor:\n"
|
136
145
|
+ f"{traceback.format_exc()}"
|
137
146
|
)
|
@@ -141,7 +150,7 @@ class FileDescriptorInterceptor:
|
|
141
150
|
except OSError as e:
|
142
151
|
if e.errno != FD_CLOSED:
|
143
152
|
warn(
|
144
|
-
|
153
|
+
"Error while trying to close the signal-write-pipe "
|
145
154
|
+ "to the intercepted file descriptor:\n"
|
146
155
|
+ f"{traceback.format_exc()}"
|
147
156
|
)
|
@@ -38,7 +38,7 @@ class RotatingFile(io.IOBase):
|
|
38
38
|
max_file_size: Optional[int] = None,
|
39
39
|
redirect_streams: bool = False,
|
40
40
|
write_timestamps: bool = False,
|
41
|
-
timestamp_format: str =
|
41
|
+
timestamp_format: Optional[str] = None,
|
42
42
|
):
|
43
43
|
"""
|
44
44
|
Create a file-like object which manages other files.
|
@@ -63,12 +63,18 @@ class RotatingFile(io.IOBase):
|
|
63
63
|
|
64
64
|
write_timestamps: bool, default False
|
65
65
|
If `True`, prepend the current UTC timestamp to each line of the file.
|
66
|
+
|
67
|
+
timestamp_format: str, default None
|
68
|
+
If `write_timestamps` is `True`, use this format for the timestamps.
|
69
|
+
Defaults to `'%Y-%m-%d %H:%M'`.
|
66
70
|
"""
|
67
71
|
self.file_path = pathlib.Path(file_path)
|
68
72
|
if num_files_to_keep is None:
|
69
73
|
num_files_to_keep = get_config('jobs', 'logs', 'num_files_to_keep')
|
70
74
|
if max_file_size is None:
|
71
75
|
max_file_size = get_config('jobs', 'logs', 'max_file_size')
|
76
|
+
if timestamp_format is None:
|
77
|
+
timestamp_format = get_config('jobs', 'logs', 'timestamps', 'format')
|
72
78
|
if num_files_to_keep < 2:
|
73
79
|
raise ValueError("At least 2 files must be kept.")
|
74
80
|
if max_file_size < 1:
|
@@ -232,10 +238,10 @@ class RotatingFile(io.IOBase):
|
|
232
238
|
|
233
239
|
|
234
240
|
def refresh_files(
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
241
|
+
self,
|
242
|
+
potential_new_len: int = 0,
|
243
|
+
start_interception: bool = False,
|
244
|
+
) -> '_io.TextUIWrapper':
|
239
245
|
"""
|
240
246
|
Check the state of the subfiles.
|
241
247
|
If the latest subfile is too large, create a new file and delete old ones.
|
@@ -339,7 +345,7 @@ class RotatingFile(io.IOBase):
|
|
339
345
|
|
340
346
|
def get_timestamp_prefix_str(self) -> str:
|
341
347
|
"""
|
342
|
-
Return the current minute
|
348
|
+
Return the current minute prefix string.
|
343
349
|
"""
|
344
350
|
return datetime.now(timezone.utc).strftime(self.timestamp_format) + ' | '
|
345
351
|
|
@@ -371,6 +377,9 @@ class RotatingFile(io.IOBase):
|
|
371
377
|
self._current_file_obj.write(data)
|
372
378
|
if suffix_str:
|
373
379
|
self._current_file_obj.write(suffix_str)
|
380
|
+
except BrokenPipeError:
|
381
|
+
warn("BrokenPipeError encountered. The daemon may have been terminated.")
|
382
|
+
return
|
374
383
|
except Exception as e:
|
375
384
|
warn(f"Failed to write to subfile:\n{traceback.format_exc()}")
|
376
385
|
self.flush()
|
@@ -565,7 +574,8 @@ class RotatingFile(io.IOBase):
|
|
565
574
|
return
|
566
575
|
|
567
576
|
self._cursor = (max_ix, position)
|
568
|
-
self._current_file_obj
|
577
|
+
if self._current_file_obj is not None:
|
578
|
+
self._current_file_obj.seek(position)
|
569
579
|
|
570
580
|
|
571
581
|
def flush(self) -> None:
|
@@ -581,10 +591,14 @@ class RotatingFile(io.IOBase):
|
|
581
591
|
if self.redirect_streams:
|
582
592
|
try:
|
583
593
|
sys.stdout.flush()
|
594
|
+
except BrokenPipeError:
|
595
|
+
pass
|
584
596
|
except Exception as e:
|
585
597
|
warn(f"Failed to flush STDOUT:\n{traceback.format_exc()}")
|
586
598
|
try:
|
587
599
|
sys.stderr.flush()
|
600
|
+
except BrokenPipeError:
|
601
|
+
pass
|
588
602
|
except Exception as e:
|
589
603
|
warn(f"Failed to flush STDERR:\n{traceback.format_exc()}")
|
590
604
|
|
@@ -596,7 +610,6 @@ class RotatingFile(io.IOBase):
|
|
596
610
|
if not self.write_timestamps:
|
597
611
|
return
|
598
612
|
|
599
|
-
threads = self.__dict__.get('_interceptor_threads', [])
|
600
613
|
self._stdout_interceptor = FileDescriptorInterceptor(
|
601
614
|
sys.stdout.fileno(),
|
602
615
|
self.get_timestamp_prefix_str,
|
@@ -639,6 +652,7 @@ class RotatingFile(io.IOBase):
|
|
639
652
|
"""
|
640
653
|
if not self.write_timestamps:
|
641
654
|
return
|
655
|
+
|
642
656
|
interceptors = self.__dict__.get('_interceptors', [])
|
643
657
|
interceptor_threads = self.__dict__.get('_interceptor_threads', [])
|
644
658
|
|
@@ -0,0 +1,121 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim:fenc=utf-8
|
4
|
+
|
5
|
+
"""
|
6
|
+
Create a file manager to pass STDIN to the Daemon.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import io
|
10
|
+
import pathlib
|
11
|
+
import time
|
12
|
+
import os
|
13
|
+
import selectors
|
14
|
+
import traceback
|
15
|
+
|
16
|
+
from meerschaum.utils.typing import Optional, Union
|
17
|
+
from meerschaum.utils.warnings import warn
|
18
|
+
|
19
|
+
|
20
|
+
class StdinFile(io.TextIOBase):
|
21
|
+
"""
|
22
|
+
Redirect user input into a Daemon's context.
|
23
|
+
"""
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
file_path: Union[pathlib.Path, str],
|
27
|
+
lock_file_path: Optional[pathlib.Path] = None,
|
28
|
+
):
|
29
|
+
if isinstance(file_path, str):
|
30
|
+
file_path = pathlib.Path(file_path)
|
31
|
+
|
32
|
+
self.file_path = file_path
|
33
|
+
self.blocking_file_path = (
|
34
|
+
lock_file_path
|
35
|
+
if lock_file_path is not None
|
36
|
+
else (file_path.parent / (file_path.name + '.block'))
|
37
|
+
)
|
38
|
+
self._file_handler = None
|
39
|
+
self._fd = None
|
40
|
+
self.sel = selectors.DefaultSelector()
|
41
|
+
|
42
|
+
@property
|
43
|
+
def file_handler(self):
|
44
|
+
"""
|
45
|
+
Return the read file handler to the provided file path.
|
46
|
+
"""
|
47
|
+
if self._file_handler is not None:
|
48
|
+
return self._file_handler
|
49
|
+
|
50
|
+
if self.file_path.exists():
|
51
|
+
self.file_path.unlink()
|
52
|
+
|
53
|
+
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
54
|
+
os.mkfifo(self.file_path.as_posix(), mode=0o600)
|
55
|
+
|
56
|
+
self._fd = os.open(self.file_path, os.O_RDONLY | os.O_NONBLOCK)
|
57
|
+
self._file_handler = os.fdopen(self._fd, 'rb', buffering=0)
|
58
|
+
self.sel.register(self._file_handler, selectors.EVENT_READ)
|
59
|
+
return self._file_handler
|
60
|
+
|
61
|
+
def write(self, data):
|
62
|
+
if isinstance(data, str):
|
63
|
+
data = data.encode('utf-8')
|
64
|
+
|
65
|
+
with open(self.file_path, 'wb') as f:
|
66
|
+
f.write(data)
|
67
|
+
|
68
|
+
def fileno(self):
|
69
|
+
fileno = self.file_handler.fileno()
|
70
|
+
return fileno
|
71
|
+
|
72
|
+
def read(self, size=-1):
|
73
|
+
"""
|
74
|
+
Read from the FIFO pipe, blocking on EOFError.
|
75
|
+
"""
|
76
|
+
_ = self.file_handler
|
77
|
+
while True:
|
78
|
+
try:
|
79
|
+
data = self._file_handler.read(size)
|
80
|
+
if data:
|
81
|
+
try:
|
82
|
+
if self.blocking_file_path.exists():
|
83
|
+
self.blocking_file_path.unlink()
|
84
|
+
except Exception:
|
85
|
+
warn(traceback.format_exc())
|
86
|
+
return data.decode('utf-8')
|
87
|
+
except (OSError, EOFError):
|
88
|
+
pass
|
89
|
+
|
90
|
+
self.blocking_file_path.touch()
|
91
|
+
time.sleep(0.1)
|
92
|
+
|
93
|
+
def readline(self, size=-1):
|
94
|
+
line = ''
|
95
|
+
while True:
|
96
|
+
data = self.read(1)
|
97
|
+
if not data or data == '\n':
|
98
|
+
break
|
99
|
+
line += data
|
100
|
+
|
101
|
+
return line
|
102
|
+
|
103
|
+
def close(self):
|
104
|
+
if self._file_handler is not None:
|
105
|
+
self.sel.unregister(self._file_handler)
|
106
|
+
self._file_handler.close()
|
107
|
+
os.close(self._fd)
|
108
|
+
self._file_handler = None
|
109
|
+
self._fd = None
|
110
|
+
|
111
|
+
super().close()
|
112
|
+
|
113
|
+
def is_open(self):
|
114
|
+
return self._file_handler is not None
|
115
|
+
|
116
|
+
|
117
|
+
def __str__(self) -> str:
|
118
|
+
return f"StdinFile('{self.file_path}')"
|
119
|
+
|
120
|
+
def __repr__(self) -> str:
|
121
|
+
return str(self)
|
@@ -10,9 +10,11 @@ from __future__ import annotations
|
|
10
10
|
import os, pathlib, shutil, json, datetime, threading, shlex
|
11
11
|
from meerschaum.utils.typing import SuccessTuple, List, Optional, Callable, Any, Dict
|
12
12
|
from meerschaum.config._paths import DAEMON_RESOURCES_PATH
|
13
|
+
from meerschaum.utils.daemon.StdinFile import StdinFile
|
13
14
|
from meerschaum.utils.daemon.Daemon import Daemon
|
14
15
|
from meerschaum.utils.daemon.RotatingFile import RotatingFile
|
15
16
|
from meerschaum.utils.daemon.FileDescriptorInterceptor import FileDescriptorInterceptor
|
17
|
+
from meerschaum.utils.daemon._names import get_new_daemon_name
|
16
18
|
|
17
19
|
|
18
20
|
def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
@@ -67,19 +69,17 @@ def daemon_entry(sysargs: Optional[List[str]] = None) -> SuccessTuple:
|
|
67
69
|
if daemon.status == 'running':
|
68
70
|
return True, f"Daemon '{daemon}' is already running."
|
69
71
|
return daemon.run(
|
70
|
-
debug
|
71
|
-
allow_dirty_run
|
72
|
+
debug=debug,
|
73
|
+
allow_dirty_run=True,
|
72
74
|
)
|
73
75
|
|
74
76
|
success_tuple = run_daemon(
|
75
77
|
entry,
|
76
78
|
filtered_sysargs,
|
77
|
-
daemon_id
|
78
|
-
label
|
79
|
-
keep_daemon_output
|
79
|
+
daemon_id=_args.get('name', None) if _args else None,
|
80
|
+
label=label,
|
81
|
+
keep_daemon_output=('--rm' not in (sysargs or [])),
|
80
82
|
)
|
81
|
-
if not isinstance(success_tuple, tuple):
|
82
|
-
success_tuple = False, str(success_tuple)
|
83
83
|
return success_tuple
|
84
84
|
|
85
85
|
|
@@ -109,25 +109,25 @@ def daemon_action(**kw) -> SuccessTuple:
|
|
109
109
|
|
110
110
|
|
111
111
|
def run_daemon(
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
112
|
+
func: Callable[[Any], Any],
|
113
|
+
*args,
|
114
|
+
daemon_id: Optional[str] = None,
|
115
|
+
keep_daemon_output: bool = True,
|
116
|
+
allow_dirty_run: bool = False,
|
117
|
+
label: Optional[str] = None,
|
118
|
+
**kw
|
119
|
+
) -> Any:
|
120
120
|
"""Execute a function as a daemon."""
|
121
121
|
daemon = Daemon(
|
122
122
|
func,
|
123
|
-
daemon_id
|
124
|
-
target_args
|
125
|
-
target_kw
|
126
|
-
label
|
123
|
+
daemon_id=daemon_id,
|
124
|
+
target_args=[arg for arg in args],
|
125
|
+
target_kw=kw,
|
126
|
+
label=label,
|
127
127
|
)
|
128
128
|
return daemon.run(
|
129
|
-
keep_daemon_output
|
130
|
-
allow_dirty_run
|
129
|
+
keep_daemon_output=keep_daemon_output,
|
130
|
+
allow_dirty_run=allow_dirty_run,
|
131
131
|
)
|
132
132
|
|
133
133
|
|
@@ -183,7 +183,11 @@ def get_daemon_ids() -> List[str]:
|
|
183
183
|
"""
|
184
184
|
Return the IDs of all daemons on disk.
|
185
185
|
"""
|
186
|
-
return
|
186
|
+
return [
|
187
|
+
daemon_dir
|
188
|
+
for daemon_dir in sorted(os.listdir(DAEMON_RESOURCES_PATH))
|
189
|
+
if (DAEMON_RESOURCES_PATH / daemon_dir / 'properties.json').exists()
|
190
|
+
]
|
187
191
|
|
188
192
|
|
189
193
|
def get_running_daemons(daemons: Optional[List[Daemon]] = None) -> List[Daemon]:
|
@@ -227,10 +231,11 @@ def get_stopped_daemons(daemons: Optional[List[Daemon]] = None) -> List[Daemon]:
|
|
227
231
|
|
228
232
|
|
229
233
|
def get_filtered_daemons(
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
"""
|
234
|
+
filter_list: Optional[List[str]] = None,
|
235
|
+
warn: bool = False,
|
236
|
+
) -> List[Daemon]:
|
237
|
+
"""
|
238
|
+
Return a list of `Daemons` filtered by a list of `daemon_ids`.
|
234
239
|
Only `Daemons` that exist are returned.
|
235
240
|
|
236
241
|
If `filter_list` is `None` or empty, return all `Daemons` (from `get_daemons()`).
|
@@ -252,13 +257,14 @@ def get_filtered_daemons(
|
|
252
257
|
if not filter_list:
|
253
258
|
daemons = get_daemons()
|
254
259
|
return [d for d in daemons if not d.hidden]
|
260
|
+
|
255
261
|
from meerschaum.utils.warnings import warn as _warn
|
256
262
|
daemons = []
|
257
263
|
for d_id in filter_list:
|
258
264
|
try:
|
259
265
|
d = Daemon(daemon_id=d_id)
|
260
266
|
_exists = d.path.exists()
|
261
|
-
except Exception
|
267
|
+
except Exception:
|
262
268
|
_exists = False
|
263
269
|
if not _exists:
|
264
270
|
if warn:
|
@@ -268,3 +274,12 @@ def get_filtered_daemons(
|
|
268
274
|
pass
|
269
275
|
daemons.append(d)
|
270
276
|
return daemons
|
277
|
+
|
278
|
+
|
279
|
+
def running_in_daemon() -> bool:
|
280
|
+
"""
|
281
|
+
Return whether the current thread is running in a Daemon context.
|
282
|
+
"""
|
283
|
+
from meerschaum.config.static import STATIC_CONFIG
|
284
|
+
daemon_env_var = STATIC_CONFIG['environment']['daemon_id']
|
285
|
+
return daemon_env_var in os.environ
|
@@ -18,7 +18,8 @@ _bank: Dict[str, Dict[str, List[str]]] = {
|
|
18
18
|
'bright', 'dark', 'neon',
|
19
19
|
],
|
20
20
|
'sizes': [
|
21
|
-
'big', 'small', 'large', 'huge', 'tiny', 'long', 'short', '
|
21
|
+
'big', 'small', 'large', 'huge', 'tiny', 'long', 'short', 'average', 'mini', 'micro',
|
22
|
+
'maximum', 'minimum', 'median',
|
22
23
|
],
|
23
24
|
'personalities': [
|
24
25
|
'groovy', 'cool', 'awesome', 'nice', 'fantastic', 'sweet', 'great', 'amazing',
|
@@ -26,29 +27,36 @@ _bank: Dict[str, Dict[str, List[str]]] = {
|
|
26
27
|
],
|
27
28
|
'emotions': [
|
28
29
|
'angry', 'happy', 'excited', 'suspicious', 'sad', 'thankful', 'grateful', 'satisfied',
|
30
|
+
'peaceful', 'ferocious', 'content',
|
29
31
|
],
|
30
32
|
'sensations': [
|
31
33
|
'sleepy', 'awake', 'alert', 'thirsty', 'comfy', 'warm', 'cold', 'chilly', 'soft',
|
32
|
-
'smooth', 'chunky',
|
34
|
+
'smooth', 'chunky', 'hungry',
|
33
35
|
],
|
34
36
|
'materials': [
|
35
37
|
'golden', 'silver', 'metal', 'plastic', 'wool', 'wooden', 'nylon', 'fuzzy', 'silky',
|
38
|
+
'suede', 'vinyl',
|
36
39
|
],
|
37
40
|
'qualities': [
|
38
41
|
'expensive', 'cheap', 'premier', 'best', 'favorite', 'better', 'good', 'affordable',
|
42
|
+
'organic', 'electric',
|
39
43
|
],
|
40
44
|
},
|
41
45
|
'nouns' : {
|
42
46
|
'animals': [
|
43
|
-
'mouse', 'fox', 'horse', '
|
47
|
+
'mouse', 'fox', 'horse', 'pig', 'hippo', 'elephant' , 'tiger', 'deer', 'salmon',
|
44
48
|
'gerbil', 'snake', 'turtle', 'rhino', 'dog', 'cat', 'giraffe', 'rabbit', 'squirrel',
|
45
49
|
'unicorn', 'lizard', 'lion', 'bear', 'gazelle', 'whale', 'dolphin', 'fish', 'butterfly',
|
46
50
|
'ladybug', 'fly', 'shrimp', 'flamingo', 'parrot', 'tuna', 'panda', 'lemur', 'duck',
|
47
51
|
'seal', 'walrus', 'seagull', 'iguana', 'salamander', 'kitten', 'puppy', 'octopus',
|
48
52
|
],
|
53
|
+
'weather': [
|
54
|
+
'rain', 'sun', 'snow', 'wind', 'tornado', 'hurricane', 'blizzard', 'monsoon', 'storm',
|
55
|
+
'shower', 'hail',
|
56
|
+
],
|
49
57
|
'plants': [
|
50
58
|
'tree', 'flower', 'vine', 'fern', 'palm', 'palmetto', 'oak', 'pine', 'rose', 'lily',
|
51
|
-
'ivy',
|
59
|
+
'ivy', 'leaf', 'shrubbery', 'acorn', 'fruit',
|
52
60
|
],
|
53
61
|
'foods': [
|
54
62
|
'pizza', 'sushi', 'apple', 'banana', 'sandwich', 'burger', 'taco', 'bratwurst',
|
@@ -74,8 +82,6 @@ _bank: Dict[str, Dict[str, List[str]]] = {
|
|
74
82
|
},
|
75
83
|
}
|
76
84
|
|
77
|
-
_disallow_combinations: List[Tuple[str, str]] = []
|
78
|
-
|
79
85
|
_adjectives: List[str]= []
|
80
86
|
for category, items in _bank['adjectives'].items():
|
81
87
|
_adjectives += items
|
@@ -84,7 +90,7 @@ _nouns: List[str] = []
|
|
84
90
|
for category, items in _bank['nouns'].items():
|
85
91
|
_nouns += items
|
86
92
|
|
87
|
-
def generate_random_name(separator: str = '
|
93
|
+
def generate_random_name(separator: str = '-'):
|
88
94
|
"""
|
89
95
|
Return a random adjective and noun combination.
|
90
96
|
|
@@ -96,12 +102,8 @@ def generate_random_name(separator: str = '_'):
|
|
96
102
|
-------
|
97
103
|
A string containing an random adjective and random noun.
|
98
104
|
"""
|
99
|
-
|
100
|
-
|
101
|
-
noun_category = random.choice(list(_bank['nouns'].keys()))
|
102
|
-
if (adjective_category, noun_category) in _disallow_combinations:
|
103
|
-
continue
|
104
|
-
break
|
105
|
+
adjective_category = random.choice(list(_bank['adjectives'].keys()))
|
106
|
+
noun_category = random.choice(list(_bank['nouns'].keys()))
|
105
107
|
return (
|
106
108
|
random.choice(_bank['adjectives'][adjective_category])
|
107
109
|
+ separator
|