python-dmon 0.2.0__py3-none-any.whl → 0.2.1__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.
- dmon/cli.py +57 -19
- dmon/config.py +30 -6
- dmon/constants.py +6 -3
- dmon/control.py +45 -6
- dmon/runner.py +16 -12
- dmon/types.py +1 -1
- {python_dmon-0.2.0.dist-info → python_dmon-0.2.1.dist-info}/METADATA +6 -6
- python_dmon-0.2.1.dist-info/RECORD +14 -0
- python_dmon-0.2.0.dist-info/RECORD +0 -14
- {python_dmon-0.2.0.dist-info → python_dmon-0.2.1.dist-info}/WHEEL +0 -0
- {python_dmon-0.2.0.dist-info → python_dmon-0.2.1.dist-info}/entry_points.txt +0 -0
dmon/cli.py
CHANGED
|
@@ -5,7 +5,7 @@ import sys
|
|
|
5
5
|
from colorama import just_fix_windows_console
|
|
6
6
|
|
|
7
7
|
from .config import check_name_in_config, get_task_config
|
|
8
|
-
from .control import list_processes, restart, start, stop, status
|
|
8
|
+
from .control import execute, list_processes, restart, start, stop, status
|
|
9
9
|
from .constants import (
|
|
10
10
|
DEFAULT_META_DIR,
|
|
11
11
|
DEFAULT_RUN_NAME,
|
|
@@ -16,14 +16,28 @@ from .constants import (
|
|
|
16
16
|
from .types import DmonTaskConfig
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def get_version():
|
|
20
|
+
# if python 3.8 or later, use importlib.metadata
|
|
21
|
+
import importlib.metadata
|
|
22
|
+
|
|
23
|
+
return importlib.metadata.version("python-dmon")
|
|
24
|
+
|
|
25
|
+
|
|
19
26
|
def main():
|
|
20
27
|
just_fix_windows_console()
|
|
21
28
|
|
|
22
29
|
parser = argparse.ArgumentParser(
|
|
23
30
|
prog="dmon",
|
|
24
|
-
description="
|
|
31
|
+
description=f"dmon v{get_version()} - Lightweight cross-platform daemon manager",
|
|
25
32
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
26
33
|
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-v",
|
|
36
|
+
"--version",
|
|
37
|
+
action="version",
|
|
38
|
+
version=get_version(),
|
|
39
|
+
)
|
|
40
|
+
|
|
27
41
|
subparsers = parser.add_subparsers(dest="command")
|
|
28
42
|
|
|
29
43
|
# start subcommand
|
|
@@ -34,7 +48,7 @@ def main():
|
|
|
34
48
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
35
49
|
)
|
|
36
50
|
sp_start.add_argument(
|
|
37
|
-
"
|
|
51
|
+
"task",
|
|
38
52
|
help="Configured task name (default: the only task if there's just one)",
|
|
39
53
|
nargs="?",
|
|
40
54
|
)
|
|
@@ -55,7 +69,7 @@ def main():
|
|
|
55
69
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
56
70
|
)
|
|
57
71
|
sp_stop.add_argument(
|
|
58
|
-
"
|
|
72
|
+
"task",
|
|
59
73
|
help="Configured task name (default: the only task if there's just one)",
|
|
60
74
|
nargs="?",
|
|
61
75
|
)
|
|
@@ -69,7 +83,7 @@ def main():
|
|
|
69
83
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
70
84
|
)
|
|
71
85
|
sp_restart.add_argument(
|
|
72
|
-
"
|
|
86
|
+
"task",
|
|
73
87
|
help="Configured task name (default: the only task if there's just one)",
|
|
74
88
|
nargs="?",
|
|
75
89
|
)
|
|
@@ -90,7 +104,7 @@ def main():
|
|
|
90
104
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
91
105
|
)
|
|
92
106
|
sp_status.add_argument(
|
|
93
|
-
"
|
|
107
|
+
"task",
|
|
94
108
|
help="Configured task name (default: the only task if there's just one)",
|
|
95
109
|
nargs="?",
|
|
96
110
|
)
|
|
@@ -162,41 +176,65 @@ def main():
|
|
|
162
176
|
help="Command (with args) to run",
|
|
163
177
|
)
|
|
164
178
|
|
|
179
|
+
sp_exec = subparsers.add_parser(
|
|
180
|
+
"exec",
|
|
181
|
+
help="Execute a configured task in the foreground",
|
|
182
|
+
description="Execute a configured task in the foreground",
|
|
183
|
+
)
|
|
184
|
+
sp_exec.add_argument(
|
|
185
|
+
"task",
|
|
186
|
+
help="Configured task name (default: the only task if there's just one)",
|
|
187
|
+
nargs="?",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
# add custom config file option
|
|
191
|
+
for sp in [sp_start, sp_stop, sp_restart, sp_status, sp_exec]:
|
|
192
|
+
sp.add_argument(
|
|
193
|
+
"--config",
|
|
194
|
+
help="Path to config file (YAML or TOML) (default: search from current directory upwards)",
|
|
195
|
+
)
|
|
196
|
+
|
|
165
197
|
args = parser.parse_args()
|
|
166
198
|
|
|
167
199
|
if args.command in ["start", "restart"]:
|
|
168
200
|
sp = sp_start if args.command == "start" else sp_restart
|
|
169
201
|
try:
|
|
170
|
-
|
|
202
|
+
task, task_cfg = get_task_config(args.task, args.config)
|
|
171
203
|
except Exception as e:
|
|
172
204
|
sp.error(str(e))
|
|
173
205
|
|
|
174
206
|
task_cfg.meta_path = (
|
|
175
|
-
args.meta_file or task_cfg.meta_path or META_PATH_TEMPLATE.format(
|
|
207
|
+
args.meta_file or task_cfg.meta_path or META_PATH_TEMPLATE.format(task=task)
|
|
176
208
|
)
|
|
177
209
|
task_cfg.log_path = (
|
|
178
|
-
args.log_file or task_cfg.log_path or LOG_PATH_TEMPLATE.format(
|
|
210
|
+
args.log_file or task_cfg.log_path or LOG_PATH_TEMPLATE.format(task=task)
|
|
179
211
|
)
|
|
180
212
|
task_cfg.rotate_log_path = (
|
|
181
|
-
task_cfg.rotate_log_path or ROTATE_LOG_PATH_TEMPLATE.format(
|
|
213
|
+
task_cfg.rotate_log_path or ROTATE_LOG_PATH_TEMPLATE.format(task=task)
|
|
182
214
|
)
|
|
183
215
|
if args.command == "start":
|
|
184
216
|
sys.exit(start(task_cfg))
|
|
185
217
|
else:
|
|
186
218
|
sys.exit(restart(task_cfg))
|
|
219
|
+
elif args.command == "exec":
|
|
220
|
+
try:
|
|
221
|
+
task, task_cfg = get_task_config(args.task, args.config)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
sp_exec.error(str(e))
|
|
224
|
+
sys.exit(execute(task_cfg))
|
|
187
225
|
elif args.command in ["stop", "status"]:
|
|
188
226
|
sp = sp_stop if args.command == "stop" else sp_status
|
|
189
227
|
if args.meta_file:
|
|
190
228
|
meta_path = args.meta_file
|
|
191
229
|
else:
|
|
192
|
-
if args.
|
|
193
|
-
|
|
230
|
+
if args.task:
|
|
231
|
+
task = args.task
|
|
194
232
|
else:
|
|
195
233
|
try:
|
|
196
|
-
|
|
234
|
+
task, _ = get_task_config(args.task, args.config)
|
|
197
235
|
except Exception as e:
|
|
198
236
|
sp.error(str(e))
|
|
199
|
-
meta_path = META_PATH_TEMPLATE.format(
|
|
237
|
+
meta_path = META_PATH_TEMPLATE.format(task=task)
|
|
200
238
|
if args.command == "stop":
|
|
201
239
|
sys.exit(stop(meta_path))
|
|
202
240
|
else:
|
|
@@ -209,18 +247,18 @@ def main():
|
|
|
209
247
|
sp_run.error("Please provide a non-empty name for the task.")
|
|
210
248
|
elif check_name_in_config(args.name):
|
|
211
249
|
sp_run.error(
|
|
212
|
-
f"
|
|
250
|
+
f"Task '{args.name}' already exists in config. Please choose another name."
|
|
213
251
|
)
|
|
214
252
|
|
|
215
253
|
task_cfg = DmonTaskConfig(
|
|
216
|
-
|
|
254
|
+
task=args.name,
|
|
217
255
|
cmd=shlex.join(args.command_list) if args.shell else args.command_list,
|
|
218
256
|
cwd=args.cwd,
|
|
219
|
-
meta_path=args.meta_file or META_PATH_TEMPLATE.format(
|
|
220
|
-
log_path=args.log_file or LOG_PATH_TEMPLATE.format(
|
|
257
|
+
meta_path=args.meta_file or META_PATH_TEMPLATE.format(task=args.name),
|
|
258
|
+
log_path=args.log_file or LOG_PATH_TEMPLATE.format(task=args.name),
|
|
221
259
|
log_rotate=args.log_rotate,
|
|
222
260
|
rotate_log_path=args.rotate_log_path
|
|
223
|
-
or ROTATE_LOG_PATH_TEMPLATE.format(
|
|
261
|
+
or ROTATE_LOG_PATH_TEMPLATE.format(task=args.name),
|
|
224
262
|
)
|
|
225
263
|
sys.exit(start(task_cfg))
|
|
226
264
|
else:
|
dmon/config.py
CHANGED
|
@@ -43,7 +43,30 @@ def find_dmon_yaml(start: Optional[Path] = None):
|
|
|
43
43
|
# raise FileNotFoundError(f"No dmon.yaml or dmon.yml found from {start} upwards.")
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def load_config():
|
|
46
|
+
def load_config(cfg_path: Optional[str] = None):
|
|
47
|
+
"""
|
|
48
|
+
Load configuration from the given path, or search for dmon.yaml or pyproject.toml.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# Load configuration from the given path
|
|
52
|
+
if cfg_path:
|
|
53
|
+
path = Path(cfg_path).resolve()
|
|
54
|
+
if not path.exists():
|
|
55
|
+
raise FileNotFoundError(f"Config file '{path}' does not exist.")
|
|
56
|
+
if path.suffix in [".yaml", ".yml"]:
|
|
57
|
+
import yaml
|
|
58
|
+
|
|
59
|
+
with path.open("r", encoding="utf-8") as f:
|
|
60
|
+
cfg = yaml.safe_load(f)
|
|
61
|
+
elif path.suffix == ".toml":
|
|
62
|
+
with path.open("rb") as f:
|
|
63
|
+
cfg = tomllib.load(f)
|
|
64
|
+
cfg = cfg.get("tool", {}).get("dmon", {})
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError("Config file must be YAML (.yaml/.yml) or TOML (.toml)")
|
|
67
|
+
return cfg, path
|
|
68
|
+
|
|
69
|
+
# No path provided, search for dmon.yaml first, then pyproject.toml
|
|
47
70
|
path = find_dmon_yaml()
|
|
48
71
|
if path:
|
|
49
72
|
import yaml
|
|
@@ -78,7 +101,7 @@ def validate_cmd_type(cmd, name: str) -> CmdType:
|
|
|
78
101
|
|
|
79
102
|
|
|
80
103
|
def validate_task(task, name: str) -> DmonTaskConfig:
|
|
81
|
-
ret = DmonTaskConfig(
|
|
104
|
+
ret = DmonTaskConfig(task=name)
|
|
82
105
|
if isinstance(task, str) or isinstance(task, list):
|
|
83
106
|
ret.cmd = validate_cmd_type(task, name)
|
|
84
107
|
elif isinstance(task, dict):
|
|
@@ -151,14 +174,15 @@ def validate_task(task, name: str) -> DmonTaskConfig:
|
|
|
151
174
|
return ret
|
|
152
175
|
|
|
153
176
|
|
|
154
|
-
def get_task_config(name: Optional[str]
|
|
177
|
+
def get_task_config(name: Optional[str], cfg_path: Optional[str]):
|
|
155
178
|
"""
|
|
156
|
-
Get the validated task configuration for the given task name
|
|
157
|
-
|
|
179
|
+
Get the validated task configuration for the given task name.
|
|
158
180
|
If name is None or empty, and there is only one task, return that task; otherwise, raise ValueError.
|
|
159
181
|
If the task is not found, or required fields are missing, raise TypeError or ValueError.
|
|
182
|
+
|
|
183
|
+
The config is loaded from the given path, or searched for dmon.yaml or pyproject.toml.
|
|
160
184
|
"""
|
|
161
|
-
cfg, path = load_config()
|
|
185
|
+
cfg, path = load_config(cfg_path)
|
|
162
186
|
tasks = cfg.get("tasks", {})
|
|
163
187
|
|
|
164
188
|
if not isinstance(tasks, dict):
|
dmon/constants.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
+
import sys
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
DEFAULT_META_DIR = Path(".dmon")
|
|
@@ -6,9 +7,11 @@ DEFAULT_LOG_DIR = Path("logs")
|
|
|
6
7
|
|
|
7
8
|
META_SUFFIX = ".meta.json"
|
|
8
9
|
|
|
9
|
-
META_PATH_TEMPLATE = str(DEFAULT_META_DIR / ("{
|
|
10
|
-
LOG_PATH_TEMPLATE = str(DEFAULT_LOG_DIR / "{
|
|
11
|
-
ROTATE_LOG_PATH_TEMPLATE = str(DEFAULT_LOG_DIR / "{
|
|
10
|
+
META_PATH_TEMPLATE = str(DEFAULT_META_DIR / ("{task}" + META_SUFFIX))
|
|
11
|
+
LOG_PATH_TEMPLATE = str(DEFAULT_LOG_DIR / "{task}.log")
|
|
12
|
+
ROTATE_LOG_PATH_TEMPLATE = str(DEFAULT_LOG_DIR / "{task}.rotate.log")
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
DEFAULT_RUN_NAME = "default_run"
|
|
16
|
+
|
|
17
|
+
ON_WINDOWS = sys.platform.startswith("win")
|
dmon/control.py
CHANGED
|
@@ -2,6 +2,7 @@ import os
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
import shlex
|
|
4
4
|
import shutil
|
|
5
|
+
import signal
|
|
5
6
|
import sys
|
|
6
7
|
import subprocess
|
|
7
8
|
import time
|
|
@@ -10,7 +11,7 @@ from typing import List
|
|
|
10
11
|
import psutil
|
|
11
12
|
from termcolor import colored
|
|
12
13
|
|
|
13
|
-
from .constants import DEFAULT_META_DIR, META_SUFFIX
|
|
14
|
+
from .constants import DEFAULT_META_DIR, META_SUFFIX, ON_WINDOWS
|
|
14
15
|
from .types import DmonTaskConfig, DmonMeta, PathType
|
|
15
16
|
from .utils import len_ansi, pad_ansi
|
|
16
17
|
|
|
@@ -66,7 +67,7 @@ def start(cfg: DmonTaskConfig):
|
|
|
66
67
|
|
|
67
68
|
# Platform-specific parameters to run the process in background detached from parent
|
|
68
69
|
kwargs = {}
|
|
69
|
-
if
|
|
70
|
+
if ON_WINDOWS:
|
|
70
71
|
# DETACHED_PROCESS = 0x00000008
|
|
71
72
|
CREATE_NO_WINDOW = 0x08000000
|
|
72
73
|
kwargs["creationflags"] = CREATE_NO_WINDOW
|
|
@@ -78,7 +79,7 @@ def start(cfg: DmonTaskConfig):
|
|
|
78
79
|
kwargs["start_new_session"] = True
|
|
79
80
|
|
|
80
81
|
meta = DmonMeta(
|
|
81
|
-
|
|
82
|
+
task=cfg.task,
|
|
82
83
|
meta_path=str(meta_path),
|
|
83
84
|
log_path=str(log_path),
|
|
84
85
|
log_rotate=cfg.log_rotate,
|
|
@@ -213,7 +214,7 @@ def stop(
|
|
|
213
214
|
meta_path.unlink(missing_ok=True)
|
|
214
215
|
return 1
|
|
215
216
|
|
|
216
|
-
if
|
|
217
|
+
if ON_WINDOWS:
|
|
217
218
|
ret = terminate_win(proc, timeout)
|
|
218
219
|
else:
|
|
219
220
|
ret = terminate_posix(proc, timeout)
|
|
@@ -416,7 +417,7 @@ def print_status(meta: DmonMeta):
|
|
|
416
417
|
|
|
417
418
|
# key-value pairs with aligned keys
|
|
418
419
|
rows = [
|
|
419
|
-
("TASK", colored(meta.
|
|
420
|
+
("TASK", colored(meta.task, "cyan", attrs=["bold"])),
|
|
420
421
|
("PID", colored(str(meta.pid), "cyan", attrs=["bold"])),
|
|
421
422
|
("STATUS", status),
|
|
422
423
|
("CMD", meta.cmd),
|
|
@@ -477,7 +478,7 @@ def print_process_table(metas: List[DmonMeta], full_width: bool = False):
|
|
|
477
478
|
ppid = "N/A"
|
|
478
479
|
rows.append(
|
|
479
480
|
(
|
|
480
|
-
colored(meta.
|
|
481
|
+
colored(meta.task, "cyan", attrs=["bold"]),
|
|
481
482
|
colored(meta.pid, "cyan", attrs=["bold"]),
|
|
482
483
|
ppid,
|
|
483
484
|
status,
|
|
@@ -540,3 +541,41 @@ def list_processes(dir: PathType, full_width: bool):
|
|
|
540
541
|
file=sys.stderr,
|
|
541
542
|
)
|
|
542
543
|
return 0
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
def execute(cfg: DmonTaskConfig):
|
|
547
|
+
"""
|
|
548
|
+
Execute the command in the foreground.
|
|
549
|
+
This is used for the 'dmon exec' command.
|
|
550
|
+
"""
|
|
551
|
+
cwd = Path(cfg.cwd).resolve()
|
|
552
|
+
|
|
553
|
+
env = None # default behavior of Popen
|
|
554
|
+
if cfg.override_env:
|
|
555
|
+
env = cfg.env
|
|
556
|
+
elif cfg.env:
|
|
557
|
+
env = {**os.environ, **cfg.env}
|
|
558
|
+
|
|
559
|
+
shell = isinstance(cfg.cmd, str)
|
|
560
|
+
|
|
561
|
+
def signal_handler(signum: int, frame) -> None:
|
|
562
|
+
if ON_WINDOWS and signum == signal.SIGINT:
|
|
563
|
+
signum = signal.SIGTERM
|
|
564
|
+
proc.send_signal(signum)
|
|
565
|
+
|
|
566
|
+
prev_handle_int = signal.signal(signal.SIGINT, signal_handler)
|
|
567
|
+
prev_handle_term = signal.signal(signal.SIGTERM, signal_handler)
|
|
568
|
+
|
|
569
|
+
proc = subprocess.Popen(
|
|
570
|
+
cfg.cmd,
|
|
571
|
+
cwd=cwd,
|
|
572
|
+
env=env,
|
|
573
|
+
shell=shell,
|
|
574
|
+
bufsize=0, # unbuffered
|
|
575
|
+
close_fds=False,
|
|
576
|
+
)
|
|
577
|
+
ret = proc.wait()
|
|
578
|
+
|
|
579
|
+
signal.signal(signal.SIGTERM, prev_handle_term)
|
|
580
|
+
signal.signal(signal.SIGINT, prev_handle_int)
|
|
581
|
+
return ret
|
dmon/runner.py
CHANGED
|
@@ -116,21 +116,13 @@ def main(
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
shell = isinstance(cmd, str)
|
|
119
|
-
# Start the child process with stdout/stderr redirected to the log
|
|
120
|
-
# env will be inherited from parent process
|
|
121
|
-
# cwd will be inherited from parent process
|
|
122
|
-
proc = subprocess.Popen(
|
|
123
|
-
cmd,
|
|
124
|
-
stdout=subprocess.PIPE,
|
|
125
|
-
stderr=subprocess.STDOUT,
|
|
126
|
-
shell=shell,
|
|
127
|
-
text=False, # binary mode
|
|
128
|
-
bufsize=0, # unbuffered
|
|
129
|
-
)
|
|
130
119
|
|
|
131
120
|
# register signal handler to terminate the child process
|
|
132
|
-
def signal_handler(signum, frame):
|
|
121
|
+
def signal_handler(signum: int, frame):
|
|
133
122
|
logger.info(f"Received signal {signum}, forwarding to child process...")
|
|
123
|
+
if sys.platform.startswith("win") and signum == signal.SIGINT:
|
|
124
|
+
signum = signal.SIGTERM
|
|
125
|
+
logger.info(f"On Windows, convert SIGINT to SIGTERM ({signum})")
|
|
134
126
|
# proc.terminate()
|
|
135
127
|
proc.send_signal(signum)
|
|
136
128
|
logger.info("Waiting for child process to exit...")
|
|
@@ -142,6 +134,18 @@ def main(
|
|
|
142
134
|
signal.signal(signal.SIGINT, signal_handler)
|
|
143
135
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
144
136
|
|
|
137
|
+
# Start the child process with stdout/stderr redirected to the log
|
|
138
|
+
# env will be inherited from parent process
|
|
139
|
+
# cwd will be inherited from parent process
|
|
140
|
+
proc = subprocess.Popen(
|
|
141
|
+
cmd,
|
|
142
|
+
stdout=subprocess.PIPE,
|
|
143
|
+
stderr=subprocess.STDOUT,
|
|
144
|
+
shell=shell,
|
|
145
|
+
text=False, # binary mode
|
|
146
|
+
bufsize=0, # unbuffered
|
|
147
|
+
)
|
|
148
|
+
|
|
145
149
|
logger.info(f"Started process {proc.pid} with command: {cmd} (shell={shell})")
|
|
146
150
|
|
|
147
151
|
make_file_dir(log_path)
|
dmon/types.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-dmon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A lightweight, cross-platform daemon manager that runs any command as a background process.
|
|
5
5
|
Keywords: python-dmon,dmon,daemon,background,detach,process management
|
|
6
6
|
Author: Atomie CHEN
|
|
@@ -12,7 +12,7 @@ Requires-Dist: termcolor>=2.4.0
|
|
|
12
12
|
Requires-Dist: tomli>=2.2.1 ; python_full_version < '3.11'
|
|
13
13
|
Requires-Python: >=3.8
|
|
14
14
|
Project-URL: Bug Tracker, https://github.com/atomiechen/python-dmon/issues
|
|
15
|
-
Project-URL: Changelog, https://github.com/atomiechen/python-dmon/blob/
|
|
15
|
+
Project-URL: Changelog, https://github.com/atomiechen/python-dmon/blob/main/CHANGELOG.md
|
|
16
16
|
Project-URL: Homepage, https://github.com/atomiechen/python-dmon
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
|
|
@@ -136,17 +136,17 @@ tasks:
|
|
|
136
136
|
env: # (default: inherit from parent process)
|
|
137
137
|
PYTHONUNBUFFERED: "1"
|
|
138
138
|
override_env: false # override parent env and only use env defined here
|
|
139
|
-
log_path: "logs/<
|
|
139
|
+
log_path: "logs/<task>.log" # path to log file
|
|
140
140
|
log_rotate: false # enable log rotation
|
|
141
141
|
log_max_size: 5 # max log file size before rotation in MB
|
|
142
|
-
rotate_log_path: "logs/<
|
|
142
|
+
rotate_log_path: "logs/<task>.rotate.log" # path to rotation log
|
|
143
143
|
rotate_log_max_size: 5 # max rotation log file size in MB
|
|
144
|
-
meta_path: ".dmon/<
|
|
144
|
+
meta_path: ".dmon/<task>.meta.json" # path to meta file
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
## Under the Hood
|
|
148
148
|
|
|
149
|
-
Each task is associated with a meta file (e.g. `.dmon/<
|
|
149
|
+
Each task is associated with a meta file (e.g. `.dmon/<task>.meta.json`) stored in the current working directory.
|
|
150
150
|
The file contains details such as the command, PID, log path, and more.
|
|
151
151
|
**Do not** modify or delete these files manually.
|
|
152
152
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
dmon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
dmon/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
|
|
3
|
+
dmon/cli.py,sha256=Dqu43_hcsx8iCJXAL1YXqBpp1jFt0Y7qBEr9mYzkAn8,8764
|
|
4
|
+
dmon/config.py,sha256=a7YM4U_iMB1wQ03NpWGAjbX-N-8Rdt9n6XTzKzeGZq4,7566
|
|
5
|
+
dmon/constants.py,sha256=uJPI66IJvT6vhamqEDeqHjZQS12RxCPe3DD5yCU0UR8,407
|
|
6
|
+
dmon/control.py,sha256=u1sz2MtxHHDqsaFyc7QaG7HFPLnAkser_8riTYxFK1A,17276
|
|
7
|
+
dmon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
dmon/runner.py,sha256=iyotNIrbO6I_u02EcqwO7R7N3BEBP6b_8eL8yb3zeJQ,6082
|
|
9
|
+
dmon/types.py,sha256=tTVoJzWk-Z8XiZCUt0VE8Z7-URpXMoS54HfVYXhW-0Q,1795
|
|
10
|
+
dmon/utils.py,sha256=zVQC9YNhMQu4hrAp9k2ds2egnbWZZv7S5w8i2oVIPJg,808
|
|
11
|
+
python_dmon-0.2.1.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
12
|
+
python_dmon-0.2.1.dist-info/entry_points.txt,sha256=rad5ugz4K_IyNXS_IrFPu0tS-UXlBCSJRhJv-OWd_yk,45
|
|
13
|
+
python_dmon-0.2.1.dist-info/METADATA,sha256=gIMrWHILmOEnjtU-gZAoYdz8JAOxFYibkos4DYs_ba0,4496
|
|
14
|
+
python_dmon-0.2.1.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
dmon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
dmon/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
|
|
3
|
-
dmon/cli.py,sha256=IVJ5jCHkaLGTQjMmWCuJbhnt4MWEOaNWQajC-mm3LUQ,7599
|
|
4
|
-
dmon/config.py,sha256=COzwN0Ms8q32pV6dqsZU59RFE2GQmqyAmfygDotc4CA,6610
|
|
5
|
-
dmon/constants.py,sha256=y5S532iTeklaR28BFbvpdOHFj1udbEJ-reD1mTCcC6E,351
|
|
6
|
-
dmon/control.py,sha256=0_NrrOA0sUM_lFHNyKGAmQsBbJhXwXfj8kxOp2S8Ni8,16311
|
|
7
|
-
dmon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
dmon/runner.py,sha256=zEW7TnHMfIw6g3UTjOTUcLFKueXpqSmNDP3ZOptXQKs,5892
|
|
9
|
-
dmon/types.py,sha256=ZtTAK-cfs2MgUFRwDH61zeIU2B4dHZDbx1644tYI1h4,1795
|
|
10
|
-
dmon/utils.py,sha256=zVQC9YNhMQu4hrAp9k2ds2egnbWZZv7S5w8i2oVIPJg,808
|
|
11
|
-
python_dmon-0.2.0.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
12
|
-
python_dmon-0.2.0.dist-info/entry_points.txt,sha256=rad5ugz4K_IyNXS_IrFPu0tS-UXlBCSJRhJv-OWd_yk,45
|
|
13
|
-
python_dmon-0.2.0.dist-info/METADATA,sha256=s1AoObf2NB_kK_Era10wpm0X6OwH-J0UVeAS8Dg6pcs,4498
|
|
14
|
-
python_dmon-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|