python-dmon 0.2.1__py3-none-any.whl → 0.2.2__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/config.py +22 -56
- dmon/control.py +5 -2
- {python_dmon-0.2.1.dist-info → python_dmon-0.2.2.dist-info}/METADATA +24 -1
- {python_dmon-0.2.1.dist-info → python_dmon-0.2.2.dist-info}/RECORD +6 -6
- {python_dmon-0.2.1.dist-info → python_dmon-0.2.2.dist-info}/WHEEL +0 -0
- {python_dmon-0.2.1.dist-info → python_dmon-0.2.2.dist-info}/entry_points.txt +0 -0
dmon/config.py
CHANGED
|
@@ -10,79 +10,45 @@ else:
|
|
|
10
10
|
from .types import CmdType, DmonTaskConfig
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def find_pyproject_toml(start: Optional[Path] = None):
|
|
14
|
-
"""
|
|
15
|
-
Search for pyproject.toml from the current directory (or specified directory) upwards.
|
|
16
|
-
Return a Path object if found.
|
|
17
|
-
"""
|
|
18
|
-
start = start or Path.cwd()
|
|
19
|
-
current = start.resolve()
|
|
20
|
-
|
|
21
|
-
for parent in [current, *current.parents]:
|
|
22
|
-
candidate = parent / "pyproject.toml"
|
|
23
|
-
if candidate.is_file():
|
|
24
|
-
return candidate
|
|
25
|
-
|
|
26
|
-
# raise FileNotFoundError(f"No pyproject.toml found from {start} upwards.")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def find_dmon_yaml(start: Optional[Path] = None):
|
|
30
|
-
"""
|
|
31
|
-
Search for dmon.yaml / dmon.yml from the current directory (or specified directory) upwards.
|
|
32
|
-
Return a Path object if found.
|
|
33
|
-
"""
|
|
34
|
-
start = start or Path.cwd()
|
|
35
|
-
current = start.resolve()
|
|
36
|
-
|
|
37
|
-
for parent in [current, *current.parents]:
|
|
38
|
-
for filename in ["dmon.yaml", "dmon.yml"]:
|
|
39
|
-
candidate = parent / filename
|
|
40
|
-
if candidate.is_file():
|
|
41
|
-
return candidate
|
|
42
|
-
|
|
43
|
-
# raise FileNotFoundError(f"No dmon.yaml or dmon.yml found from {start} upwards.")
|
|
44
|
-
|
|
45
|
-
|
|
46
13
|
def load_config(cfg_path: Optional[str] = None):
|
|
47
14
|
"""
|
|
48
|
-
Load configuration from the given path, or search
|
|
15
|
+
Load configuration from the given path, or search it from the current working directory upwards.
|
|
49
16
|
"""
|
|
50
17
|
|
|
51
|
-
# Load configuration from the given path
|
|
52
18
|
if cfg_path:
|
|
19
|
+
# Load configuration from the given path
|
|
53
20
|
path = Path(cfg_path).resolve()
|
|
54
21
|
if not path.exists():
|
|
55
22
|
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
|
|
70
|
-
path = find_dmon_yaml()
|
|
71
|
-
if path:
|
|
72
|
-
import yaml
|
|
73
|
-
|
|
74
|
-
with path.open("r", encoding="utf-8") as f:
|
|
75
|
-
cfg = yaml.safe_load(f)
|
|
76
23
|
else:
|
|
77
|
-
path
|
|
24
|
+
# No path provided, search for config files
|
|
25
|
+
# starting from the current working directory upwards
|
|
26
|
+
current = Path.cwd().resolve()
|
|
27
|
+
path = None
|
|
28
|
+
for parent in [current, *current.parents]:
|
|
29
|
+
for filename in ["dmon.yaml", "dmon.yml", "pyproject.toml"]:
|
|
30
|
+
path = parent / filename
|
|
31
|
+
if path.is_file():
|
|
32
|
+
break
|
|
33
|
+
else:
|
|
34
|
+
continue # Only break out of the inner loop if a file was found
|
|
35
|
+
break # Break out of the outer loop if a file was found
|
|
78
36
|
if not path:
|
|
79
37
|
raise FileNotFoundError(
|
|
80
38
|
"No dmon.yaml or pyproject.toml found in current or any parent directory."
|
|
81
39
|
)
|
|
82
40
|
|
|
41
|
+
if path.suffix in [".yaml", ".yml"]:
|
|
42
|
+
import yaml
|
|
43
|
+
|
|
44
|
+
with path.open("r", encoding="utf-8") as f:
|
|
45
|
+
cfg = yaml.safe_load(f)
|
|
46
|
+
elif path.suffix == ".toml":
|
|
83
47
|
with path.open("rb") as f:
|
|
84
48
|
cfg = tomllib.load(f)
|
|
85
49
|
cfg = cfg.get("tool", {}).get("dmon", {})
|
|
50
|
+
else:
|
|
51
|
+
raise ValueError("Config file must be YAML (.yaml/.yml) or TOML (.toml)")
|
|
86
52
|
return cfg, path
|
|
87
53
|
|
|
88
54
|
|
dmon/control.py
CHANGED
|
@@ -525,14 +525,14 @@ def print_process_table(metas: List[DmonMeta], full_width: bool = False):
|
|
|
525
525
|
|
|
526
526
|
def list_processes(dir: PathType, full_width: bool):
|
|
527
527
|
target_dmon_dir = Path(dir).resolve()
|
|
528
|
-
metas = []
|
|
528
|
+
metas: List[DmonMeta] = []
|
|
529
529
|
if target_dmon_dir.exists() and target_dmon_dir.is_dir():
|
|
530
530
|
for meta_file in target_dmon_dir.glob(f"*{META_SUFFIX}"):
|
|
531
531
|
meta = DmonMeta.load(meta_file)
|
|
532
532
|
if meta is not None:
|
|
533
533
|
metas.append(meta)
|
|
534
534
|
# sort by name (case-insensitive)
|
|
535
|
-
metas.sort(key=lambda m: m.
|
|
535
|
+
metas.sort(key=lambda m: m.task.lower())
|
|
536
536
|
n_task = len(metas)
|
|
537
537
|
processes = print_process_table(metas, full_width)
|
|
538
538
|
n_proc = len(processes)
|
|
@@ -557,6 +557,9 @@ def execute(cfg: DmonTaskConfig):
|
|
|
557
557
|
env = {**os.environ, **cfg.env}
|
|
558
558
|
|
|
559
559
|
shell = isinstance(cfg.cmd, str)
|
|
560
|
+
if ON_WINDOWS and isinstance(cfg.cmd, list) and len(cfg.cmd) > 0:
|
|
561
|
+
# On Windows, use full path for the executable when shell=False
|
|
562
|
+
cfg.cmd[0] = shutil.which(cfg.cmd[0]) or cfg.cmd[0]
|
|
560
563
|
|
|
561
564
|
def signal_handler(signum: int, frame) -> None:
|
|
562
565
|
if ON_WINDOWS and signum == signal.SIGINT:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-dmon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -86,6 +86,9 @@ dmon stop app
|
|
|
86
86
|
|
|
87
87
|
# Check task status
|
|
88
88
|
dmon status app
|
|
89
|
+
|
|
90
|
+
# Execute a task in the foreground (useful for debugging)
|
|
91
|
+
dmon exec app
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
If only one task is defined in the config file, you can omit the task name:
|
|
@@ -94,6 +97,26 @@ If only one task is defined in the config file, you can omit the task name:
|
|
|
94
97
|
dmon start
|
|
95
98
|
dmon stop
|
|
96
99
|
dmon status
|
|
100
|
+
dmon exec
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
You can use `--config` to specify a custom config file:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
dmon start --config /path/to/dmon.yaml app # YAML
|
|
107
|
+
dmon start --config /path/to/pyproject.toml app # or TOML
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
And yes, you can use `dmon` to run in a nested manner:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
tasks:
|
|
114
|
+
app: ["python", "-u", "server.py"]
|
|
115
|
+
nested: pwd && dmon exec app # nest `dmon exec`
|
|
116
|
+
subdir_task1:
|
|
117
|
+
cwd: /path/to/dir
|
|
118
|
+
cmd: ["dmon", "exec", "app"] # run task defined in another folder
|
|
119
|
+
subdir_task2: dmon exec app --config /path/to/dir/dmon.yaml # like above
|
|
97
120
|
```
|
|
98
121
|
|
|
99
122
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
dmon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
dmon/__main__.py,sha256=wu5N2wk8mvBgyvr2ghmQf4prezAe0_i-p123VVreyYc,62
|
|
3
3
|
dmon/cli.py,sha256=Dqu43_hcsx8iCJXAL1YXqBpp1jFt0Y7qBEr9mYzkAn8,8764
|
|
4
|
-
dmon/config.py,sha256=
|
|
4
|
+
dmon/config.py,sha256=Q8TvcYDwUTlL_8bcjxVsA-lLE_AlcSmxYpbnMaCkFrw,6650
|
|
5
5
|
dmon/constants.py,sha256=uJPI66IJvT6vhamqEDeqHjZQS12RxCPe3DD5yCU0UR8,407
|
|
6
|
-
dmon/control.py,sha256=
|
|
6
|
+
dmon/control.py,sha256=KpGNPPsuAsDNX3FTJxnGNnt9f4Q18omvG42zO1CwTy4,17494
|
|
7
7
|
dmon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
dmon/runner.py,sha256=iyotNIrbO6I_u02EcqwO7R7N3BEBP6b_8eL8yb3zeJQ,6082
|
|
9
9
|
dmon/types.py,sha256=tTVoJzWk-Z8XiZCUt0VE8Z7-URpXMoS54HfVYXhW-0Q,1795
|
|
10
10
|
dmon/utils.py,sha256=zVQC9YNhMQu4hrAp9k2ds2egnbWZZv7S5w8i2oVIPJg,808
|
|
11
|
-
python_dmon-0.2.
|
|
12
|
-
python_dmon-0.2.
|
|
13
|
-
python_dmon-0.2.
|
|
14
|
-
python_dmon-0.2.
|
|
11
|
+
python_dmon-0.2.2.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
12
|
+
python_dmon-0.2.2.dist-info/entry_points.txt,sha256=rad5ugz4K_IyNXS_IrFPu0tS-UXlBCSJRhJv-OWd_yk,45
|
|
13
|
+
python_dmon-0.2.2.dist-info/METADATA,sha256=tncJt5ouNhwjFYaGHEKEQgxas34QDLW6IRY1Qd2bO6g,5106
|
|
14
|
+
python_dmon-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|