pmflow 1.2.0__tar.gz → 1.2.3__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.
- {pmflow-1.2.0 → pmflow-1.2.3}/PKG-INFO +29 -8
- pmflow-1.2.3/README.md +73 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/commands/create.py +17 -8
- pmflow-1.2.3/pm/commands/kill.py +127 -0
- pmflow-1.2.3/pm/commands/ls.py +67 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/main.py +1 -2
- pmflow-1.2.3/pm/schema.py +6 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/utils.py +17 -3
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/PKG-INFO +29 -8
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/SOURCES.txt +1 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pyproject.toml +1 -1
- pmflow-1.2.0/README.md +0 -52
- pmflow-1.2.0/pm/commands/kill.py +0 -65
- pmflow-1.2.0/pm/commands/ls.py +0 -47
- {pmflow-1.2.0 → pmflow-1.2.3}/LICENSE +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/MANIFEST.in +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/__init__.py +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/commands/__init__.py +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pm/settings.py +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/dependency_links.txt +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/entry_points.txt +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/requires.txt +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/pmflow.egg-info/top_level.txt +0 -0
- {pmflow-1.2.0 → pmflow-1.2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pmflow
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Manages processes
|
|
5
5
|
Author-email: "Md. Mahmudul Hasan Riyad" <mhriyad98@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -46,6 +46,7 @@ Provides-Extra: dev
|
|
|
46
46
|
pip install pmflow
|
|
47
47
|
```
|
|
48
48
|
## Commands
|
|
49
|
+
### <^> Main commands
|
|
49
50
|
create: Creates a new process
|
|
50
51
|
|
|
51
52
|
Required arguments:
|
|
@@ -56,26 +57,46 @@ Optional arguments:
|
|
|
56
57
|
- --group or -g : str (default: process_id)
|
|
57
58
|
- --relation or -r : "parent" | "child" (default: "parent")
|
|
58
59
|
- --verbose or -v: (default: false)
|
|
60
|
+
|
|
61
|
+
Note: A child process must have a group name and the group must have a parent process.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
59
64
|
```
|
|
60
|
-
pm create "<
|
|
65
|
+
pm create "<command>"
|
|
61
66
|
or
|
|
62
|
-
pm create "<
|
|
67
|
+
pm create "<command>" -n "<process name>" -g "<group name>" -r "child"
|
|
63
68
|
```
|
|
64
69
|
ls: List all managed processes
|
|
65
70
|
|
|
66
71
|
Optional arguments:
|
|
67
|
-
- --json or -j (default: false)
|
|
72
|
+
- --json or -j: bool (default: false)
|
|
73
|
+
- --group or -g: str (default: None)
|
|
74
|
+
- --running or -r: bool (default: false)
|
|
75
|
+
Example:
|
|
68
76
|
```
|
|
69
77
|
pm ls
|
|
70
78
|
pm ls -j
|
|
79
|
+
pm ls -g <group_name>
|
|
80
|
+
pm ls -j -g <group_name>
|
|
81
|
+
pm ls -r
|
|
71
82
|
```
|
|
72
|
-
Kill process, kills the process and also removes it from the json file.
|
|
83
|
+
kill: Kill process, kills the process and also removes it from the json file.
|
|
84
|
+
|
|
85
|
+
Optional arguments:
|
|
86
|
+
- pid (no flag needed): int (default: 0) kills one process if it's a child process. Otherwise, kills the terminates
|
|
87
|
+
- --group or -g: str (default: None) kills one group if exist
|
|
88
|
+
- --child or -c: bool (default: False) kills all child process in a group
|
|
89
|
+
- --all or -a : bool (default: false) kills all the process
|
|
90
|
+
|
|
91
|
+
Note: Other than -c only one options can be used at a time. -c flag must be used with -g
|
|
73
92
|
|
|
93
|
+
Example:
|
|
74
94
|
```
|
|
75
95
|
pm kill <PID>
|
|
76
|
-
pm
|
|
96
|
+
pm -g <group_name>
|
|
97
|
+
pm -a
|
|
77
98
|
```
|
|
78
|
-
|
|
99
|
+
### <^> Additional commands
|
|
79
100
|
Recreate all process managed by the tool:
|
|
80
101
|
```
|
|
81
102
|
pm recreate
|
|
@@ -86,5 +107,5 @@ pm pause <PID>
|
|
|
86
107
|
```
|
|
87
108
|
respawn all paused process
|
|
88
109
|
```
|
|
89
|
-
pm respawn
|
|
110
|
+
pm respawn
|
|
90
111
|
```
|
pmflow-1.2.3/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# PMFlow
|
|
2
|
+
<p style="font-size:15px;">This is a simple process managing tool</p>
|
|
3
|
+
|
|
4
|
+
<p style="font-size:15px;">This tools keeps the information of the processes it manages in a json file created at it's installation location</p>
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
```
|
|
8
|
+
pip install pmflow
|
|
9
|
+
```
|
|
10
|
+
## Commands
|
|
11
|
+
### <^> Main commands
|
|
12
|
+
create: Creates a new process
|
|
13
|
+
|
|
14
|
+
Required arguments:
|
|
15
|
+
- command: str (default: None)
|
|
16
|
+
|
|
17
|
+
Optional arguments:
|
|
18
|
+
- --name or -n : str (default: None)
|
|
19
|
+
- --group or -g : str (default: process_id)
|
|
20
|
+
- --relation or -r : "parent" | "child" (default: "parent")
|
|
21
|
+
- --verbose or -v: (default: false)
|
|
22
|
+
|
|
23
|
+
Note: A child process must have a group name and the group must have a parent process.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
```
|
|
27
|
+
pm create "<command>"
|
|
28
|
+
or
|
|
29
|
+
pm create "<command>" -n "<process name>" -g "<group name>" -r "child"
|
|
30
|
+
```
|
|
31
|
+
ls: List all managed processes
|
|
32
|
+
|
|
33
|
+
Optional arguments:
|
|
34
|
+
- --json or -j: bool (default: false)
|
|
35
|
+
- --group or -g: str (default: None)
|
|
36
|
+
- --running or -r: bool (default: false)
|
|
37
|
+
Example:
|
|
38
|
+
```
|
|
39
|
+
pm ls
|
|
40
|
+
pm ls -j
|
|
41
|
+
pm ls -g <group_name>
|
|
42
|
+
pm ls -j -g <group_name>
|
|
43
|
+
pm ls -r
|
|
44
|
+
```
|
|
45
|
+
kill: Kill process, kills the process and also removes it from the json file.
|
|
46
|
+
|
|
47
|
+
Optional arguments:
|
|
48
|
+
- pid (no flag needed): int (default: 0) kills one process if it's a child process. Otherwise, kills the terminates
|
|
49
|
+
- --group or -g: str (default: None) kills one group if exist
|
|
50
|
+
- --child or -c: bool (default: False) kills all child process in a group
|
|
51
|
+
- --all or -a : bool (default: false) kills all the process
|
|
52
|
+
|
|
53
|
+
Note: Other than -c only one options can be used at a time. -c flag must be used with -g
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
```
|
|
57
|
+
pm kill <PID>
|
|
58
|
+
pm -g <group_name>
|
|
59
|
+
pm -a
|
|
60
|
+
```
|
|
61
|
+
### <^> Additional commands
|
|
62
|
+
Recreate all process managed by the tool:
|
|
63
|
+
```
|
|
64
|
+
pm recreate
|
|
65
|
+
```
|
|
66
|
+
Pause a process
|
|
67
|
+
```
|
|
68
|
+
pm pause <PID>
|
|
69
|
+
```
|
|
70
|
+
respawn all paused process
|
|
71
|
+
```
|
|
72
|
+
pm respawn
|
|
73
|
+
```
|
|
@@ -13,12 +13,7 @@ from pm.settings import state
|
|
|
13
13
|
from typing import Optional
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from typing_extensions import Annotated
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class Relation(str, Enum):
|
|
19
|
-
PARENT = 'parent'
|
|
20
|
-
CHILD = 'child'
|
|
21
|
-
|
|
16
|
+
from pm.schema import Relation
|
|
22
17
|
|
|
23
18
|
def create(command: Annotated[str, typer.Argument()],
|
|
24
19
|
name: Annotated[Optional[str], typer.Option("--name","-n")] = None,
|
|
@@ -27,15 +22,29 @@ def create(command: Annotated[str, typer.Argument()],
|
|
|
27
22
|
verbose: Annotated [bool, typer.Option("--verbose", "-v")] = False) -> int:
|
|
28
23
|
|
|
29
24
|
"""Create a new subprocess and optionally assign a name."""
|
|
25
|
+
|
|
26
|
+
if relation == Relation.CHILD:
|
|
27
|
+
if not group:
|
|
28
|
+
typer.echo(f"Error 800: Please specify an existing group of a parent process")
|
|
29
|
+
raise typer.Exit(code=1)
|
|
30
|
+
elif group not in state.get_parents_groupname():
|
|
31
|
+
typer.echo(f"Error 801: No parent group with this name exist. "
|
|
32
|
+
f"Please specify an existing group of a parent process")
|
|
33
|
+
raise typer.Exit(code=1)
|
|
34
|
+
|
|
30
35
|
proc = subprocess.Popen(command, shell=True)
|
|
31
36
|
pid = proc.pid
|
|
32
37
|
|
|
33
|
-
if
|
|
34
|
-
|
|
38
|
+
if relation == Relation.PARENT:
|
|
39
|
+
if not group:
|
|
40
|
+
group = str(pid)
|
|
41
|
+
elif state.is_group_exist(group):
|
|
42
|
+
typer.echo(f"Error 802: A group can have only one parent process.")
|
|
35
43
|
|
|
36
44
|
data = {
|
|
37
45
|
"command": command,
|
|
38
46
|
"name": name,
|
|
47
|
+
"status": "running",
|
|
39
48
|
"group": group,
|
|
40
49
|
"relation": relation,
|
|
41
50
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module is related to stopping a process's from running or deleting it altogether
|
|
3
|
+
The commands it manages are
|
|
4
|
+
- pause
|
|
5
|
+
- kill
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
import psutil
|
|
10
|
+
import signal
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from typing_extensions import Annotated
|
|
13
|
+
from pm.settings import state
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def pause(pid: int):
|
|
17
|
+
"""Pause a subprocess and all its children by PID."""
|
|
18
|
+
pid_str = str(pid)
|
|
19
|
+
if pid_str in state.processes:
|
|
20
|
+
try:
|
|
21
|
+
process = psutil.Process(int(pid))
|
|
22
|
+
all_processes = [process] + process.children(recursive=True)
|
|
23
|
+
for proc in all_processes:
|
|
24
|
+
proc.send_signal(signal.SIGSTOP)
|
|
25
|
+
typer.echo(f"Process {pid} and its child processes have been paused.")
|
|
26
|
+
except psutil.NoSuchProcess:
|
|
27
|
+
typer.echo("Process not found.")
|
|
28
|
+
else:
|
|
29
|
+
typer.echo("Process not managed by this tool.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def kill(pid: Annotated[Optional[int], typer.Argument()] = 0,
|
|
33
|
+
group: Annotated[Optional[str], typer.Option("--group", "-g")] = None,
|
|
34
|
+
child: Annotated[Optional[bool], typer.Option("--child", "-c")] = False,
|
|
35
|
+
all: Annotated[Optional[bool], typer.Option("--all", "-a")] = False,):
|
|
36
|
+
"""
|
|
37
|
+
Kill a subprocess by PID, group name.
|
|
38
|
+
|
|
39
|
+
params:
|
|
40
|
+
pid: int -> kill a process by it's pid.
|
|
41
|
+
group: str -> kill a process by it's group name.'
|
|
42
|
+
child: bool -> kill all child process of a group. This can only be used with --group or -g.
|
|
43
|
+
all: bool -> kill all existing process.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# validating arguments so that only one argument is given otherwise throw error
|
|
48
|
+
args_given = sum([pid != 0, group is not None, all is not False])
|
|
49
|
+
|
|
50
|
+
if args_given == 0:
|
|
51
|
+
typer.echo("Error 1100: You must specify exactly one of: PID, group, or --all.", err=True)
|
|
52
|
+
raise typer.Exit(code=1)
|
|
53
|
+
|
|
54
|
+
if args_given > 1:
|
|
55
|
+
typer.echo("Error 1200: You can only specify one of: PID, group, or --all.", err=True)
|
|
56
|
+
raise typer.Exit(code=1)
|
|
57
|
+
|
|
58
|
+
# Delete process with the flag given
|
|
59
|
+
# For pid
|
|
60
|
+
if pid:
|
|
61
|
+
pid_str = str(pid)
|
|
62
|
+
|
|
63
|
+
if pid_str in state.get_processes().keys():
|
|
64
|
+
if state.get_processes()[pid_str]["relation"] == "parent":
|
|
65
|
+
typer.echo(f"Process {pid} is a parent process. Killing it with all it's children...")
|
|
66
|
+
process_group = state.get_a_group(state.get_processes()[pid_str]["group"])
|
|
67
|
+
if process_group:
|
|
68
|
+
kill_group_process(process_group)
|
|
69
|
+
for pid in process_group.keys():
|
|
70
|
+
state.remove_process(pid)
|
|
71
|
+
else:
|
|
72
|
+
try:
|
|
73
|
+
process = psutil.Process(int(pid))
|
|
74
|
+
for child in process.children(recursive=True):
|
|
75
|
+
child.terminate()
|
|
76
|
+
process.terminate()
|
|
77
|
+
state.remove_process(pid_str)
|
|
78
|
+
typer.echo(f"Process {pid} killed.")
|
|
79
|
+
except psutil.NoSuchProcess:
|
|
80
|
+
state.remove_process(pid_str)
|
|
81
|
+
typer.echo("Process not found. Removed from the state file.")
|
|
82
|
+
else:
|
|
83
|
+
typer.echo("Process not managed by this tool.")
|
|
84
|
+
|
|
85
|
+
# For group flag --group or -g
|
|
86
|
+
if group:
|
|
87
|
+
process_group = state.get_a_group(group)
|
|
88
|
+
|
|
89
|
+
if child:
|
|
90
|
+
process_group = {pid: data for pid, data in process_group.items() if data["relation"] == "child"}
|
|
91
|
+
|
|
92
|
+
if process_group:
|
|
93
|
+
kill_group_process(process_group)
|
|
94
|
+
for pid in process_group.keys():
|
|
95
|
+
state.remove_process(pid)
|
|
96
|
+
|
|
97
|
+
# For all flag --all or -a
|
|
98
|
+
if all:
|
|
99
|
+
for pid in state.processes.keys():
|
|
100
|
+
try:
|
|
101
|
+
process = psutil.Process(int(pid))
|
|
102
|
+
for child in process.children(recursive=True):
|
|
103
|
+
child.terminate()
|
|
104
|
+
process.terminate()
|
|
105
|
+
typer.echo(f"Process {pid} terminated.")
|
|
106
|
+
except psutil.NoSuchProcess:
|
|
107
|
+
typer.echo(f"Process {pid} not found.")
|
|
108
|
+
except Exception as e:
|
|
109
|
+
typer.echo(f"Error terminating process {pid}: {str(e)}")
|
|
110
|
+
|
|
111
|
+
state.remove_all_processes()
|
|
112
|
+
typer.echo("All processes have been terminated and removed from the state.")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def kill_group_process(processes: dict):
|
|
116
|
+
|
|
117
|
+
for pid in processes.keys():
|
|
118
|
+
try:
|
|
119
|
+
process = psutil.Process(int(pid))
|
|
120
|
+
for child in process.children(recursive=True):
|
|
121
|
+
child.terminate()
|
|
122
|
+
process.terminate()
|
|
123
|
+
typer.echo(f"Process {pid} terminated.")
|
|
124
|
+
except psutil.NoSuchProcess:
|
|
125
|
+
typer.echo(f"Process {pid} not found. Removed from state.")
|
|
126
|
+
except Exception as e:
|
|
127
|
+
typer.echo(f"Error terminating process {pid}: {str(e)}")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module manages handles output presentation of the process's.
|
|
3
|
+
The command it manages are
|
|
4
|
+
- ls
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
import psutil
|
|
9
|
+
import json
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
from pm.settings import state
|
|
13
|
+
from typing_extensions import Annotated
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def ls(json_output: Annotated[bool, typer.Option("--json", "-j")] = False,
|
|
17
|
+
group_name: Annotated[str, typer.Option("--group", "-g")] = None,
|
|
18
|
+
running: Annotated[bool, typer.Option("--running", "-r")] = False) -> None:
|
|
19
|
+
"""List all managed subprocesses."""
|
|
20
|
+
|
|
21
|
+
# Update status of each process
|
|
22
|
+
for pid, properties in state.get_processes().items():
|
|
23
|
+
try:
|
|
24
|
+
process = psutil.Process(int(pid))
|
|
25
|
+
if process.status() == psutil.STATUS_STOPPED:
|
|
26
|
+
status = 'paused'
|
|
27
|
+
else:
|
|
28
|
+
status = 'running'
|
|
29
|
+
except psutil.NoSuchProcess:
|
|
30
|
+
status = "doesn't exist"
|
|
31
|
+
|
|
32
|
+
state.update_process(pid, "status", status)
|
|
33
|
+
|
|
34
|
+
# Console output block
|
|
35
|
+
if group_name:
|
|
36
|
+
process_dict = state.get_a_group(group_name)
|
|
37
|
+
if not process_dict:
|
|
38
|
+
typer.echo(f"Error 1000: No process group with group name {group_name} exist")
|
|
39
|
+
raise typer.Exit(code=1)
|
|
40
|
+
else:
|
|
41
|
+
process_dict = state.get_processes()
|
|
42
|
+
|
|
43
|
+
if running:
|
|
44
|
+
process_dict ={pid: data for pid, data in process_dict.items() if data["status"] == "running"}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if json_output:
|
|
48
|
+
json_output = json.dumps(process_dict)
|
|
49
|
+
typer.echo(json_output)
|
|
50
|
+
|
|
51
|
+
else:
|
|
52
|
+
table = Table()
|
|
53
|
+
table.add_column("PID", justify="center", style="#00ffee")
|
|
54
|
+
table.add_column("Name", justify="center", style="#f7d59e")
|
|
55
|
+
table.add_column("Status", justify="center", style="#e2fa2a")
|
|
56
|
+
table.add_column("Group", justify="center", style="#00ffee")
|
|
57
|
+
table.add_column("Relation", justify="center", style="#f700ff")
|
|
58
|
+
table.add_column("Command", justify="center", style="#00ff26")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
for pid, properties in process_dict.items():
|
|
62
|
+
|
|
63
|
+
table.add_row(pid, properties["name"], properties["status"], properties["group"],
|
|
64
|
+
properties["relation"],properties["command"])
|
|
65
|
+
|
|
66
|
+
console = Console()
|
|
67
|
+
console.print(table)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import typer
|
|
3
3
|
|
|
4
4
|
from pm.commands.create import create, recreate, respawn
|
|
5
|
-
from pm.commands.kill import pause, kill
|
|
5
|
+
from pm.commands.kill import pause, kill
|
|
6
6
|
from pm.commands.ls import ls
|
|
7
7
|
|
|
8
8
|
app = typer.Typer()
|
|
@@ -20,7 +20,6 @@ app.command()(respawn)
|
|
|
20
20
|
# Commands related to deletion of a process
|
|
21
21
|
app.command()(pause)
|
|
22
22
|
app.command()(kill)
|
|
23
|
-
app.command()(kill_all)
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
# Commands related to process presentation
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
+
from typing import List, Dict
|
|
4
5
|
|
|
5
6
|
import typer
|
|
6
7
|
|
|
8
|
+
from pm.schema import Relation
|
|
7
9
|
|
|
8
10
|
class StateManager:
|
|
9
11
|
def __init__(self, STATE_FILE):
|
|
@@ -25,9 +27,6 @@ class StateManager:
|
|
|
25
27
|
with open(self.STATE_FILE, "w") as file:
|
|
26
28
|
json.dump(self.processes, file)
|
|
27
29
|
|
|
28
|
-
def get_processes(self):
|
|
29
|
-
return self.processes
|
|
30
|
-
|
|
31
30
|
def add_process(self, pid, data):
|
|
32
31
|
self.processes[str(pid)] = data
|
|
33
32
|
self.save()
|
|
@@ -48,6 +47,21 @@ class StateManager:
|
|
|
48
47
|
self.processes = bulk_data
|
|
49
48
|
self.save()
|
|
50
49
|
|
|
50
|
+
def get_processes(self):
|
|
51
|
+
return self.processes
|
|
52
|
+
|
|
53
|
+
def get_parents_groupname(self) -> List[Relation]:
|
|
54
|
+
parent_groups = [process_meta_data["group"] for process_meta_data in self.processes.values()
|
|
55
|
+
if process_meta_data["relation"] == Relation.PARENT]
|
|
56
|
+
return parent_groups
|
|
57
|
+
|
|
58
|
+
def get_a_group(self, group_name: str) -> Dict:
|
|
59
|
+
group_process = {pid: data for pid, data in self.processes.items() if data["group"] == group_name}
|
|
60
|
+
return group_process
|
|
61
|
+
|
|
62
|
+
def is_group_exist(self, group_name: str) -> bool:
|
|
63
|
+
return group_name in self.get_parents_groupname()
|
|
64
|
+
|
|
51
65
|
|
|
52
66
|
|
|
53
67
|
def load_state(path):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pmflow
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.3
|
|
4
4
|
Summary: Manages processes
|
|
5
5
|
Author-email: "Md. Mahmudul Hasan Riyad" <mhriyad98@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -46,6 +46,7 @@ Provides-Extra: dev
|
|
|
46
46
|
pip install pmflow
|
|
47
47
|
```
|
|
48
48
|
## Commands
|
|
49
|
+
### <^> Main commands
|
|
49
50
|
create: Creates a new process
|
|
50
51
|
|
|
51
52
|
Required arguments:
|
|
@@ -56,26 +57,46 @@ Optional arguments:
|
|
|
56
57
|
- --group or -g : str (default: process_id)
|
|
57
58
|
- --relation or -r : "parent" | "child" (default: "parent")
|
|
58
59
|
- --verbose or -v: (default: false)
|
|
60
|
+
|
|
61
|
+
Note: A child process must have a group name and the group must have a parent process.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
59
64
|
```
|
|
60
|
-
pm create "<
|
|
65
|
+
pm create "<command>"
|
|
61
66
|
or
|
|
62
|
-
pm create "<
|
|
67
|
+
pm create "<command>" -n "<process name>" -g "<group name>" -r "child"
|
|
63
68
|
```
|
|
64
69
|
ls: List all managed processes
|
|
65
70
|
|
|
66
71
|
Optional arguments:
|
|
67
|
-
- --json or -j (default: false)
|
|
72
|
+
- --json or -j: bool (default: false)
|
|
73
|
+
- --group or -g: str (default: None)
|
|
74
|
+
- --running or -r: bool (default: false)
|
|
75
|
+
Example:
|
|
68
76
|
```
|
|
69
77
|
pm ls
|
|
70
78
|
pm ls -j
|
|
79
|
+
pm ls -g <group_name>
|
|
80
|
+
pm ls -j -g <group_name>
|
|
81
|
+
pm ls -r
|
|
71
82
|
```
|
|
72
|
-
Kill process, kills the process and also removes it from the json file.
|
|
83
|
+
kill: Kill process, kills the process and also removes it from the json file.
|
|
84
|
+
|
|
85
|
+
Optional arguments:
|
|
86
|
+
- pid (no flag needed): int (default: 0) kills one process if it's a child process. Otherwise, kills the terminates
|
|
87
|
+
- --group or -g: str (default: None) kills one group if exist
|
|
88
|
+
- --child or -c: bool (default: False) kills all child process in a group
|
|
89
|
+
- --all or -a : bool (default: false) kills all the process
|
|
90
|
+
|
|
91
|
+
Note: Other than -c only one options can be used at a time. -c flag must be used with -g
|
|
73
92
|
|
|
93
|
+
Example:
|
|
74
94
|
```
|
|
75
95
|
pm kill <PID>
|
|
76
|
-
pm
|
|
96
|
+
pm -g <group_name>
|
|
97
|
+
pm -a
|
|
77
98
|
```
|
|
78
|
-
|
|
99
|
+
### <^> Additional commands
|
|
79
100
|
Recreate all process managed by the tool:
|
|
80
101
|
```
|
|
81
102
|
pm recreate
|
|
@@ -86,5 +107,5 @@ pm pause <PID>
|
|
|
86
107
|
```
|
|
87
108
|
respawn all paused process
|
|
88
109
|
```
|
|
89
|
-
pm respawn
|
|
110
|
+
pm respawn
|
|
90
111
|
```
|
pmflow-1.2.0/README.md
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# PMFlow
|
|
2
|
-
<p style="font-size:15px;">This is a simple process managing tool</p>
|
|
3
|
-
|
|
4
|
-
<p style="font-size:15px;">This tools keeps the information of the processes it manages in a json file created at it's installation location</p>
|
|
5
|
-
|
|
6
|
-
## Installation
|
|
7
|
-
```
|
|
8
|
-
pip install pmflow
|
|
9
|
-
```
|
|
10
|
-
## Commands
|
|
11
|
-
create: Creates a new process
|
|
12
|
-
|
|
13
|
-
Required arguments:
|
|
14
|
-
- command: str (default: None)
|
|
15
|
-
|
|
16
|
-
Optional arguments:
|
|
17
|
-
- --name or -n : str (default: None)
|
|
18
|
-
- --group or -g : str (default: process_id)
|
|
19
|
-
- --relation or -r : "parent" | "child" (default: "parent")
|
|
20
|
-
- --verbose or -v: (default: false)
|
|
21
|
-
```
|
|
22
|
-
pm create "<your command>"
|
|
23
|
-
or
|
|
24
|
-
pm create "<your command>" --name "<your process name>"
|
|
25
|
-
```
|
|
26
|
-
ls: List all managed processes
|
|
27
|
-
|
|
28
|
-
Optional arguments:
|
|
29
|
-
- --json or -j (default: false)
|
|
30
|
-
```
|
|
31
|
-
pm ls
|
|
32
|
-
pm ls -j
|
|
33
|
-
```
|
|
34
|
-
Kill process, kills the process and also removes it from the json file.
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
pm kill <PID>
|
|
38
|
-
pm kill-all
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Recreate all process managed by the tool:
|
|
42
|
-
```
|
|
43
|
-
pm recreate
|
|
44
|
-
```
|
|
45
|
-
Pause a process
|
|
46
|
-
```
|
|
47
|
-
pm pause <PID>
|
|
48
|
-
```
|
|
49
|
-
respawn all paused process
|
|
50
|
-
```
|
|
51
|
-
pm respawn-all
|
|
52
|
-
```
|
pmflow-1.2.0/pm/commands/kill.py
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module is related to stopping a process's from running or deleting it altogether
|
|
3
|
-
The commands it manages are
|
|
4
|
-
- pause
|
|
5
|
-
- kill
|
|
6
|
-
- kill-all
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import typer
|
|
10
|
-
import psutil
|
|
11
|
-
import signal
|
|
12
|
-
from pm.settings import state
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def pause(pid: int):
|
|
16
|
-
"""Pause a subprocess and all its children by PID."""
|
|
17
|
-
pid_str = str(pid)
|
|
18
|
-
if pid_str in state.processes:
|
|
19
|
-
try:
|
|
20
|
-
process = psutil.Process(int(pid))
|
|
21
|
-
all_processes = [process] + process.children(recursive=True)
|
|
22
|
-
for proc in all_processes:
|
|
23
|
-
proc.send_signal(signal.SIGSTOP)
|
|
24
|
-
typer.echo(f"Process {pid} and its child processes have been paused.")
|
|
25
|
-
except psutil.NoSuchProcess:
|
|
26
|
-
typer.echo("Process not found.")
|
|
27
|
-
else:
|
|
28
|
-
typer.echo("Process not managed by this tool.")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def kill(pid: int):
|
|
32
|
-
"""Kill a subprocess by PID."""
|
|
33
|
-
pid_str = str(pid)
|
|
34
|
-
if pid_str in state.processes:
|
|
35
|
-
try:
|
|
36
|
-
process = psutil.Process(int(pid))
|
|
37
|
-
for child in process.children(recursive=True):
|
|
38
|
-
child.terminate()
|
|
39
|
-
process.terminate()
|
|
40
|
-
state.remove_process(pid_str)
|
|
41
|
-
typer.echo(f"Process {pid} killed.")
|
|
42
|
-
except psutil.NoSuchProcess:
|
|
43
|
-
state.remove_process(pid_str)
|
|
44
|
-
typer.echo("Process not found. Removed from the state file.")
|
|
45
|
-
else:
|
|
46
|
-
typer.echo("Process not managed by this tool.")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def kill_all():
|
|
50
|
-
"""Kill all managed processes and clear the state."""
|
|
51
|
-
|
|
52
|
-
for pid in state.processes.keys():
|
|
53
|
-
try:
|
|
54
|
-
process = psutil.Process(int(pid))
|
|
55
|
-
for child in process.children(recursive=True):
|
|
56
|
-
child.terminate()
|
|
57
|
-
process.terminate()
|
|
58
|
-
typer.echo(f"Process {pid} terminated.")
|
|
59
|
-
except psutil.NoSuchProcess:
|
|
60
|
-
typer.echo(f"Process {pid} not found.")
|
|
61
|
-
except Exception as e:
|
|
62
|
-
typer.echo(f"Error terminating process {pid}: {str(e)}")
|
|
63
|
-
|
|
64
|
-
state.remove_all_processes()
|
|
65
|
-
typer.echo("All processes have been terminated and removed from the state.")
|
pmflow-1.2.0/pm/commands/ls.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
This module manages handles output presentation of the process's.
|
|
3
|
-
The command it manages are
|
|
4
|
-
- ls
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import typer
|
|
8
|
-
import psutil
|
|
9
|
-
import json
|
|
10
|
-
from rich.console import Console
|
|
11
|
-
from rich.table import Table
|
|
12
|
-
from pm.settings import state
|
|
13
|
-
from typing_extensions import Annotated
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def ls(json_output: Annotated[bool, typer.Option("--json", "-j")] = False) -> None:
|
|
17
|
-
"""List all managed subprocesses."""
|
|
18
|
-
|
|
19
|
-
if json_output:
|
|
20
|
-
json_output = json.dumps(state.get_processes())
|
|
21
|
-
typer.echo(json_output)
|
|
22
|
-
|
|
23
|
-
else:
|
|
24
|
-
table = Table()
|
|
25
|
-
table.add_column("PID", justify="center", style="#00ffee")
|
|
26
|
-
table.add_column("Name", justify="center", style="#f7d59e")
|
|
27
|
-
table.add_column("Status", justify="center", style="#e2fa2a")
|
|
28
|
-
table.add_column("Group", justify="center", style="#00ffee")
|
|
29
|
-
table.add_column("Relation", justify="center", style="#f700ff")
|
|
30
|
-
table.add_column("Command", justify="center", style="#00ff26")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
for pid, properties in state.get_processes().items():
|
|
34
|
-
try:
|
|
35
|
-
process = psutil.Process(int(pid))
|
|
36
|
-
if process.status() == psutil.STATUS_STOPPED:
|
|
37
|
-
status = 'paused'
|
|
38
|
-
else:
|
|
39
|
-
status = 'running'
|
|
40
|
-
except psutil.NoSuchProcess:
|
|
41
|
-
status = "doesn't exist"
|
|
42
|
-
|
|
43
|
-
table.add_row(pid, properties["name"], status, properties["group"],
|
|
44
|
-
properties["relation"],properties["command"])
|
|
45
|
-
|
|
46
|
-
console = Console()
|
|
47
|
-
console.print(table)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|