py-simple-service-manager 0.1.0__tar.gz

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 (35) hide show
  1. py_simple_service_manager-0.1.0/LICENSE +21 -0
  2. py_simple_service_manager-0.1.0/PKG-INFO +186 -0
  3. py_simple_service_manager-0.1.0/README.md +162 -0
  4. py_simple_service_manager-0.1.0/pyproject.toml +41 -0
  5. py_simple_service_manager-0.1.0/setup.cfg +4 -0
  6. py_simple_service_manager-0.1.0/src/py_simple_service_manager/__init__.py +6 -0
  7. py_simple_service_manager-0.1.0/src/py_simple_service_manager/__main__.py +4 -0
  8. py_simple_service_manager-0.1.0/src/py_simple_service_manager/admin.py +21 -0
  9. py_simple_service_manager-0.1.0/src/py_simple_service_manager/bootstrap.py +39 -0
  10. py_simple_service_manager-0.1.0/src/py_simple_service_manager/cli.py +301 -0
  11. py_simple_service_manager-0.1.0/src/py_simple_service_manager/gui.py +364 -0
  12. py_simple_service_manager-0.1.0/src/py_simple_service_manager/manager.py +199 -0
  13. py_simple_service_manager-0.1.0/src/py_simple_service_manager/models.py +151 -0
  14. py_simple_service_manager-0.1.0/src/py_simple_service_manager/paths.py +142 -0
  15. py_simple_service_manager-0.1.0/src/py_simple_service_manager/platforms/__init__.py +21 -0
  16. py_simple_service_manager-0.1.0/src/py_simple_service_manager/platforms/base.py +35 -0
  17. py_simple_service_manager-0.1.0/src/py_simple_service_manager/platforms/linux.py +93 -0
  18. py_simple_service_manager-0.1.0/src/py_simple_service_manager/platforms/macos.py +79 -0
  19. py_simple_service_manager-0.1.0/src/py_simple_service_manager/platforms/windows.py +131 -0
  20. py_simple_service_manager-0.1.0/src/py_simple_service_manager/process.py +85 -0
  21. py_simple_service_manager-0.1.0/src/py_simple_service_manager/runner.py +207 -0
  22. py_simple_service_manager-0.1.0/src/py_simple_service_manager/store.py +79 -0
  23. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/PKG-INFO +186 -0
  24. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/SOURCES.txt +33 -0
  25. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/dependency_links.txt +1 -0
  26. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/entry_points.txt +4 -0
  27. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/requires.txt +3 -0
  28. py_simple_service_manager-0.1.0/src/py_simple_service_manager.egg-info/top_level.txt +1 -0
  29. py_simple_service_manager-0.1.0/tests/test_bootstrap.py +27 -0
  30. py_simple_service_manager-0.1.0/tests/test_cli.py +27 -0
  31. py_simple_service_manager-0.1.0/tests/test_manager.py +65 -0
  32. py_simple_service_manager-0.1.0/tests/test_models.py +31 -0
  33. py_simple_service_manager-0.1.0/tests/test_runner.py +102 -0
  34. py_simple_service_manager-0.1.0/tests/test_store.py +34 -0
  35. py_simple_service_manager-0.1.0/tests/test_windows_backend.py +44 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 GGN_2015
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-simple-service-manager
3
+ Version: 0.1.0
4
+ Summary: Cross-platform command-as-service manager for Windows, Linux systemd, and macOS launchd.
5
+ Author: GGN_2015
6
+ License-Expression: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Topic :: System :: Boot
16
+ Classifier: Topic :: System :: Systems Administration
17
+ Classifier: Topic :: Utilities
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: admin
22
+ Requires-Dist: py-admin-launch>=0.1.3; extra == "admin"
23
+ Dynamic: license-file
24
+
25
+ # py-simple-service-manager
26
+
27
+ `py-simple-service-manager` lets you manage a one-line command as a service on
28
+ Linux, Windows, and macOS.
29
+
30
+ The same Python runner handles command execution, retry attempts, status files,
31
+ and stdout/stderr logs. Platform backends only register and control the runner:
32
+
33
+ - Linux: `systemd` units controlled by `systemctl`
34
+ - Windows: Task Scheduler tasks controlled by `schtasks` and PowerShell
35
+ - macOS: `launchd` agents/daemons controlled by `launchctl`
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ python -m pip install -e .
41
+ ```
42
+
43
+ Optional administrator relaunch support:
44
+
45
+ ```bash
46
+ python -m pip install -e ".[admin]"
47
+ ```
48
+
49
+ The `admin` extra uses
50
+ [`py-admin-launch`](https://pypi.org/project/py-admin-launch/) for Windows UAC,
51
+ Linux `pkexec`/`sudo`, and macOS `osascript`/`sudo`.
52
+
53
+ ## Startup modes
54
+
55
+ - `manual`: do not start at boot.
56
+ - `auto`: start once at boot after network components are available.
57
+ - `auto-retry`: start at boot and retry after `K` seconds up to `N` times when
58
+ the command exits with a non-zero code.
59
+
60
+ The retry count is handled by this package's runner, so it works consistently
61
+ across all supported platforms.
62
+
63
+ `pssm logs` and the GUI log tabs read the current log files, not only completed
64
+ runs. The runner flushes stdout/stderr to disk as bytes arrive, and Python
65
+ commands are launched with unbuffered output automatically.
66
+
67
+ ## Usage
68
+
69
+ Register a manual service:
70
+
71
+ ```bash
72
+ pssm add api -- python -m http.server 9000
73
+ ```
74
+
75
+ Register a boot service:
76
+
77
+ ```bash
78
+ pssm --scope system --elevate add api --mode auto -- python -m http.server 9000
79
+ ```
80
+
81
+ Register a boot service with retry:
82
+
83
+ ```bash
84
+ pssm --scope system --elevate add worker --mode auto-retry --retry-delay 10 --retry-limit 5 -- python worker.py
85
+ ```
86
+
87
+ Change startup mode later:
88
+
89
+ ```bash
90
+ pssm mode worker manual
91
+ pssm --scope system --elevate mode worker auto-retry --retry-delay 10 --retry-limit 5
92
+ ```
93
+
94
+ Inspect and control services:
95
+
96
+ ```bash
97
+ pssm list
98
+ pssm status worker
99
+ pssm logs worker
100
+ pssm logs worker --stream stderr --lines 100
101
+ pssm start worker
102
+ pssm stop worker --force
103
+ pssm remove worker
104
+ ```
105
+
106
+ Edit the same fields exposed by the GUI:
107
+
108
+ ```bash
109
+ pssm edit worker --command "python worker.py --queue default" --cwd /path/to/app
110
+ pssm edit worker --mode auto-retry --retry-delay 10 --retry-limit 5
111
+ pssm edit worker --clear-cwd
112
+ ```
113
+
114
+ Open the GUI:
115
+
116
+ ```bash
117
+ pssm gui
118
+ pssm-gui
119
+ ```
120
+
121
+ The GUI supports listing services, creating services, editing command/startup
122
+ mode/retry settings/working directory, deleting services, starting and stopping
123
+ services, and viewing stdout/stderr. Anything the GUI can change is also
124
+ available through the CLI.
125
+
126
+ Show storage paths and platform details:
127
+
128
+ ```bash
129
+ pssm doctor
130
+ ```
131
+
132
+ `--scope` can be `user`, `system`, or `auto`. `auto` uses `system` when the
133
+ current process is already elevated, otherwise `user`.
134
+
135
+ ## Platform notes
136
+
137
+ ### Linux
138
+
139
+ Linux requires `systemd` and `systemctl`.
140
+
141
+ - User scope writes units to `~/.config/systemd/user`.
142
+ - System scope writes units to `/etc/systemd/system`.
143
+ - Boot startup uses `network-online.target` with `Wants=` and `After=`.
144
+
145
+ For user units that should survive logout, enable lingering outside this tool:
146
+
147
+ ```bash
148
+ loginctl enable-linger "$USER"
149
+ ```
150
+
151
+ ### Windows
152
+
153
+ Windows uses Task Scheduler.
154
+
155
+ - Manual services can be registered in user scope.
156
+ - Boot startup requires system scope and elevation:
157
+
158
+ ```bash
159
+ pssm --scope system --elevate add name --mode auto -- your-command
160
+ ```
161
+
162
+ System-scope tasks run as `SYSTEM` with highest available privileges. Make sure
163
+ the command and Python environment are available to that account.
164
+
165
+ ### macOS
166
+
167
+ macOS uses `launchd`.
168
+
169
+ - User scope writes LaunchAgents to `~/Library/LaunchAgents`.
170
+ - System scope writes LaunchDaemons to `/Library/LaunchDaemons`.
171
+ - Boot startup maps to `RunAtLoad=true`.
172
+
173
+ ## Development
174
+
175
+ Run tests from source:
176
+
177
+ ```bash
178
+ PYTHONPATH=src python -m unittest discover -s tests -v
179
+ ```
180
+
181
+ On Windows PowerShell:
182
+
183
+ ```powershell
184
+ $env:PYTHONPATH = "src"
185
+ python -m unittest discover -s tests -v
186
+ ```
@@ -0,0 +1,162 @@
1
+ # py-simple-service-manager
2
+
3
+ `py-simple-service-manager` lets you manage a one-line command as a service on
4
+ Linux, Windows, and macOS.
5
+
6
+ The same Python runner handles command execution, retry attempts, status files,
7
+ and stdout/stderr logs. Platform backends only register and control the runner:
8
+
9
+ - Linux: `systemd` units controlled by `systemctl`
10
+ - Windows: Task Scheduler tasks controlled by `schtasks` and PowerShell
11
+ - macOS: `launchd` agents/daemons controlled by `launchctl`
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ python -m pip install -e .
17
+ ```
18
+
19
+ Optional administrator relaunch support:
20
+
21
+ ```bash
22
+ python -m pip install -e ".[admin]"
23
+ ```
24
+
25
+ The `admin` extra uses
26
+ [`py-admin-launch`](https://pypi.org/project/py-admin-launch/) for Windows UAC,
27
+ Linux `pkexec`/`sudo`, and macOS `osascript`/`sudo`.
28
+
29
+ ## Startup modes
30
+
31
+ - `manual`: do not start at boot.
32
+ - `auto`: start once at boot after network components are available.
33
+ - `auto-retry`: start at boot and retry after `K` seconds up to `N` times when
34
+ the command exits with a non-zero code.
35
+
36
+ The retry count is handled by this package's runner, so it works consistently
37
+ across all supported platforms.
38
+
39
+ `pssm logs` and the GUI log tabs read the current log files, not only completed
40
+ runs. The runner flushes stdout/stderr to disk as bytes arrive, and Python
41
+ commands are launched with unbuffered output automatically.
42
+
43
+ ## Usage
44
+
45
+ Register a manual service:
46
+
47
+ ```bash
48
+ pssm add api -- python -m http.server 9000
49
+ ```
50
+
51
+ Register a boot service:
52
+
53
+ ```bash
54
+ pssm --scope system --elevate add api --mode auto -- python -m http.server 9000
55
+ ```
56
+
57
+ Register a boot service with retry:
58
+
59
+ ```bash
60
+ pssm --scope system --elevate add worker --mode auto-retry --retry-delay 10 --retry-limit 5 -- python worker.py
61
+ ```
62
+
63
+ Change startup mode later:
64
+
65
+ ```bash
66
+ pssm mode worker manual
67
+ pssm --scope system --elevate mode worker auto-retry --retry-delay 10 --retry-limit 5
68
+ ```
69
+
70
+ Inspect and control services:
71
+
72
+ ```bash
73
+ pssm list
74
+ pssm status worker
75
+ pssm logs worker
76
+ pssm logs worker --stream stderr --lines 100
77
+ pssm start worker
78
+ pssm stop worker --force
79
+ pssm remove worker
80
+ ```
81
+
82
+ Edit the same fields exposed by the GUI:
83
+
84
+ ```bash
85
+ pssm edit worker --command "python worker.py --queue default" --cwd /path/to/app
86
+ pssm edit worker --mode auto-retry --retry-delay 10 --retry-limit 5
87
+ pssm edit worker --clear-cwd
88
+ ```
89
+
90
+ Open the GUI:
91
+
92
+ ```bash
93
+ pssm gui
94
+ pssm-gui
95
+ ```
96
+
97
+ The GUI supports listing services, creating services, editing command/startup
98
+ mode/retry settings/working directory, deleting services, starting and stopping
99
+ services, and viewing stdout/stderr. Anything the GUI can change is also
100
+ available through the CLI.
101
+
102
+ Show storage paths and platform details:
103
+
104
+ ```bash
105
+ pssm doctor
106
+ ```
107
+
108
+ `--scope` can be `user`, `system`, or `auto`. `auto` uses `system` when the
109
+ current process is already elevated, otherwise `user`.
110
+
111
+ ## Platform notes
112
+
113
+ ### Linux
114
+
115
+ Linux requires `systemd` and `systemctl`.
116
+
117
+ - User scope writes units to `~/.config/systemd/user`.
118
+ - System scope writes units to `/etc/systemd/system`.
119
+ - Boot startup uses `network-online.target` with `Wants=` and `After=`.
120
+
121
+ For user units that should survive logout, enable lingering outside this tool:
122
+
123
+ ```bash
124
+ loginctl enable-linger "$USER"
125
+ ```
126
+
127
+ ### Windows
128
+
129
+ Windows uses Task Scheduler.
130
+
131
+ - Manual services can be registered in user scope.
132
+ - Boot startup requires system scope and elevation:
133
+
134
+ ```bash
135
+ pssm --scope system --elevate add name --mode auto -- your-command
136
+ ```
137
+
138
+ System-scope tasks run as `SYSTEM` with highest available privileges. Make sure
139
+ the command and Python environment are available to that account.
140
+
141
+ ### macOS
142
+
143
+ macOS uses `launchd`.
144
+
145
+ - User scope writes LaunchAgents to `~/Library/LaunchAgents`.
146
+ - System scope writes LaunchDaemons to `/Library/LaunchDaemons`.
147
+ - Boot startup maps to `RunAtLoad=true`.
148
+
149
+ ## Development
150
+
151
+ Run tests from source:
152
+
153
+ ```bash
154
+ PYTHONPATH=src python -m unittest discover -s tests -v
155
+ ```
156
+
157
+ On Windows PowerShell:
158
+
159
+ ```powershell
160
+ $env:PYTHONPATH = "src"
161
+ python -m unittest discover -s tests -v
162
+ ```
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "py-simple-service-manager"
7
+ version = "0.1.0"
8
+ description = "Cross-platform command-as-service manager for Windows, Linux systemd, and macOS launchd."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "GGN_2015" }]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Operating System :: MacOS",
19
+ "Operating System :: Microsoft :: Windows",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Topic :: System :: Boot",
24
+ "Topic :: System :: Systems Administration",
25
+ "Topic :: Utilities",
26
+ ]
27
+ dependencies = []
28
+
29
+ [project.optional-dependencies]
30
+ admin = ["py-admin-launch>=0.1.3"]
31
+
32
+ [project.scripts]
33
+ pssm = "py_simple_service_manager.cli:main"
34
+ py-simple-service-manager = "py_simple_service_manager.cli:main"
35
+ pssm-gui = "py_simple_service_manager.gui:main"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """Cross-platform command-as-service manager."""
2
+
3
+ from .models import ServiceConfig, StartupMode
4
+
5
+ __all__ = ["ServiceConfig", "StartupMode"]
6
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ from typing import Sequence
6
+
7
+ from .paths import is_admin
8
+
9
+
10
+ def relaunch_with_admin(argv: Sequence[str]) -> int:
11
+ if is_admin():
12
+ return -1
13
+ try:
14
+ from py_admin_launch import launch
15
+ except ImportError as exc:
16
+ raise RuntimeError(
17
+ "admin relaunch requires py-admin-launch; install with "
18
+ "'pip install py-simple-service-manager[admin]'"
19
+ ) from exc
20
+ result = launch([sys.executable, "-m", "py_simple_service_manager", *argv], cwd=os.getcwd(), wait=True)
21
+ return int(result.returncode or 0)
@@ -0,0 +1,39 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from .paths import AppLayout
6
+
7
+
8
+ def bootstrap_path(layout: AppLayout, name: str) -> Path:
9
+ return layout.generated_dir / f"{name}-runner.py"
10
+
11
+
12
+ def write_runner_bootstrap(layout: AppLayout, name: str) -> Path:
13
+ path = bootstrap_path(layout, name)
14
+ package_parent = Path(__file__).resolve().parent.parent
15
+ path.parent.mkdir(parents=True, exist_ok=True)
16
+ path.write_text(
17
+ "\n".join(
18
+ [
19
+ "from __future__ import annotations",
20
+ "",
21
+ "import sys",
22
+ "",
23
+ f"sys.path.insert(0, {ascii(str(package_parent))})",
24
+ "from py_simple_service_manager.runner import main",
25
+ "",
26
+ "if __name__ == '__main__':",
27
+ " raise SystemExit(main())",
28
+ "",
29
+ ]
30
+ ),
31
+ encoding="utf-8",
32
+ )
33
+ return path
34
+
35
+
36
+ def delete_runner_bootstrap(layout: AppLayout, name: str) -> None:
37
+ path = bootstrap_path(layout, name)
38
+ if path.exists():
39
+ path.unlink()