python-dmon 0.2.2__py3-none-any.whl → 0.2.3__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 +1 -1
- dmon/config.py +30 -13
- {python_dmon-0.2.2.dist-info → python_dmon-0.2.3.dist-info}/METADATA +46 -5
- {python_dmon-0.2.2.dist-info → python_dmon-0.2.3.dist-info}/RECORD +6 -6
- {python_dmon-0.2.2.dist-info → python_dmon-0.2.3.dist-info}/entry_points.txt +1 -0
- {python_dmon-0.2.2.dist-info → python_dmon-0.2.3.dist-info}/WHEEL +0 -0
dmon/cli.py
CHANGED
|
@@ -191,7 +191,7 @@ def main():
|
|
|
191
191
|
for sp in [sp_start, sp_stop, sp_restart, sp_status, sp_exec]:
|
|
192
192
|
sp.add_argument(
|
|
193
193
|
"--config",
|
|
194
|
-
help="Path to config file
|
|
194
|
+
help="Path to config file or the directory containing it (default: search from current directory upwards)",
|
|
195
195
|
)
|
|
196
196
|
|
|
197
197
|
args = parser.parse_args()
|
dmon/config.py
CHANGED
|
@@ -10,6 +10,21 @@ else:
|
|
|
10
10
|
from .types import CmdType, DmonTaskConfig
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def search_config(start_dir: Path, recursive: bool) -> Optional[Path]:
|
|
14
|
+
"""
|
|
15
|
+
Search for dmon.yaml, dmon.yml, or pyproject.toml from the given directory upwards.
|
|
16
|
+
Return the path if found, None otherwise.
|
|
17
|
+
"""
|
|
18
|
+
current = start_dir.resolve()
|
|
19
|
+
directories = [current] if not recursive else [current, *current.parents]
|
|
20
|
+
for parent in directories:
|
|
21
|
+
for filename in ["dmon.yaml", "dmon.yml", "pyproject.toml"]:
|
|
22
|
+
path = parent / filename
|
|
23
|
+
if path.is_file():
|
|
24
|
+
return path
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
13
28
|
def load_config(cfg_path: Optional[str] = None):
|
|
14
29
|
"""
|
|
15
30
|
Load configuration from the given path, or search it from the current working directory upwards.
|
|
@@ -19,20 +34,20 @@ def load_config(cfg_path: Optional[str] = None):
|
|
|
19
34
|
# Load configuration from the given path
|
|
20
35
|
path = Path(cfg_path).resolve()
|
|
21
36
|
if not path.exists():
|
|
22
|
-
raise FileNotFoundError(
|
|
37
|
+
raise FileNotFoundError(
|
|
38
|
+
f"Config file or directory '{path}' does not exist."
|
|
39
|
+
)
|
|
40
|
+
elif path.is_dir():
|
|
41
|
+
# If it's a directory, search for config files in it
|
|
42
|
+
result = search_config(path, recursive=False)
|
|
43
|
+
if not result:
|
|
44
|
+
raise FileNotFoundError(
|
|
45
|
+
f"No dmon.yaml or pyproject.toml found in directory '{path}'."
|
|
46
|
+
)
|
|
47
|
+
path = result
|
|
23
48
|
else:
|
|
24
|
-
# No path provided, search
|
|
25
|
-
|
|
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
|
|
49
|
+
# No path provided, search from the current working directory upwards
|
|
50
|
+
path = search_config(Path.cwd(), recursive=True)
|
|
36
51
|
if not path:
|
|
37
52
|
raise FileNotFoundError(
|
|
38
53
|
"No dmon.yaml or pyproject.toml found in current or any parent directory."
|
|
@@ -154,6 +169,8 @@ def get_task_config(name: Optional[str], cfg_path: Optional[str]):
|
|
|
154
169
|
if not isinstance(tasks, dict):
|
|
155
170
|
raise TypeError("'tasks' must be a table")
|
|
156
171
|
|
|
172
|
+
name = name or cfg.get("default_task", None)
|
|
173
|
+
|
|
157
174
|
if not name:
|
|
158
175
|
if len(tasks) == 0:
|
|
159
176
|
raise ValueError(f"No task found in {path}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: python-dmon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
@@ -41,10 +41,35 @@ It is a Python-based and more powerful successor to the [handy-backend shell scr
|
|
|
41
41
|
|
|
42
42
|
## Installation
|
|
43
43
|
|
|
44
|
+
`python-dmon` is available on [PyPI](https://pypi.org/project/python-dmon/):
|
|
45
|
+
|
|
44
46
|
```sh
|
|
45
47
|
pip install python-dmon
|
|
46
48
|
```
|
|
47
49
|
|
|
50
|
+
We recommend installing into an isolated environment, e.g., with `uv` / `pipx`:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
# Install globally with uv tool
|
|
54
|
+
uv tool install python-dmon
|
|
55
|
+
|
|
56
|
+
# Or with pipx
|
|
57
|
+
pipx install python-dmon
|
|
58
|
+
|
|
59
|
+
# Add as a dev dependency in your project
|
|
60
|
+
uv add --dev python-dmon
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You can also invoke without installing:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
# With uvx (uv tool run)
|
|
67
|
+
uvx python-dmon
|
|
68
|
+
|
|
69
|
+
# Or with pipx
|
|
70
|
+
pipx run python-dmon
|
|
71
|
+
```
|
|
72
|
+
|
|
48
73
|
To get the latest features, install from source:
|
|
49
74
|
|
|
50
75
|
```sh
|
|
@@ -70,7 +95,7 @@ app = ["python", "-u", "server.py"]
|
|
|
70
95
|
```
|
|
71
96
|
|
|
72
97
|
Commands can be a single string (run in shell), or list of strings (exec form).
|
|
73
|
-
See [Example
|
|
98
|
+
See [Example Configuration](#example-configuration) for more configuration options.
|
|
74
99
|
|
|
75
100
|
|
|
76
101
|
### Run tasks
|
|
@@ -91,7 +116,7 @@ dmon status app
|
|
|
91
116
|
dmon exec app
|
|
92
117
|
```
|
|
93
118
|
|
|
94
|
-
If only one task is defined in the config file, you can omit the task name:
|
|
119
|
+
If you have defined `default_task`, or only one task is defined in the config file, you can omit the task name:
|
|
95
120
|
|
|
96
121
|
```sh
|
|
97
122
|
dmon start
|
|
@@ -100,11 +125,12 @@ dmon status
|
|
|
100
125
|
dmon exec
|
|
101
126
|
```
|
|
102
127
|
|
|
103
|
-
You can use `--config` to specify a custom config file:
|
|
128
|
+
You can use `--config` to specify a custom config file or the directory containing it:
|
|
104
129
|
|
|
105
130
|
```sh
|
|
106
131
|
dmon start --config /path/to/dmon.yaml app # YAML
|
|
107
132
|
dmon start --config /path/to/pyproject.toml app # or TOML
|
|
133
|
+
dmon start --config /path/to/dir app # dir with `dmon.y(a)ml` or `pyproject.toml`
|
|
108
134
|
```
|
|
109
135
|
|
|
110
136
|
And yes, you can use `dmon` to run in a nested manner:
|
|
@@ -144,7 +170,7 @@ dmon list
|
|
|
144
170
|
```
|
|
145
171
|
|
|
146
172
|
|
|
147
|
-
## Example
|
|
173
|
+
## Example Configuration
|
|
148
174
|
|
|
149
175
|
A task can be a **string**, **list**, or **dictionary**.
|
|
150
176
|
|
|
@@ -165,8 +191,23 @@ tasks:
|
|
|
165
191
|
rotate_log_path: "logs/<task>.rotate.log" # path to rotation log
|
|
166
192
|
rotate_log_max_size: 5 # max rotation log file size in MB
|
|
167
193
|
meta_path: ".dmon/<task>.meta.json" # path to meta file
|
|
194
|
+
default_task: your_task_name # the default task name
|
|
168
195
|
```
|
|
169
196
|
|
|
197
|
+
In TOML, write like this:
|
|
198
|
+
|
|
199
|
+
```toml
|
|
200
|
+
[tool.dmon.tasks]
|
|
201
|
+
your_task_name = { cmd = [
|
|
202
|
+
"python", "-u", "server.py"
|
|
203
|
+
], ... }
|
|
204
|
+
another_task = "cd subdir && ls && bash start.sh"
|
|
205
|
+
|
|
206
|
+
[tool.dmon]
|
|
207
|
+
default_task = "your_task_name"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
|
|
170
211
|
## Under the Hood
|
|
171
212
|
|
|
172
213
|
Each task is associated with a meta file (e.g. `.dmon/<task>.meta.json`) stored in the current working directory.
|
|
@@ -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
|
-
dmon/cli.py,sha256=
|
|
4
|
-
dmon/config.py,sha256=
|
|
3
|
+
dmon/cli.py,sha256=CdlgGTWWbTbJ-yz1Cct1EoJtaNGI3zs_3puoMRtrqbg,8780
|
|
4
|
+
dmon/config.py,sha256=RXcQEgyXSmx_sIXzcrMnc_XkyV0uc20MEv5DTNxFHrw,7204
|
|
5
5
|
dmon/constants.py,sha256=uJPI66IJvT6vhamqEDeqHjZQS12RxCPe3DD5yCU0UR8,407
|
|
6
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.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
12
|
+
python_dmon-0.2.3.dist-info/entry_points.txt,sha256=1XmEpyjShANXEHMIMa4p3WXpcllIZfGt5DQyTFuSI74,78
|
|
13
|
+
python_dmon-0.2.3.dist-info/METADATA,sha256=7k6z_ZfbmzpHEAiUKY_O0MQVwkEuWE53PLPz1Th66wA,5984
|
|
14
|
+
python_dmon-0.2.3.dist-info/RECORD,,
|
|
File without changes
|