diff --git a/processpy/PManager.py b/processpy/PManager.py new file mode 100644 index 0000000..705cecc --- /dev/null +++ b/processpy/PManager.py @@ -0,0 +1,54 @@ +import multiprocessing +from typing import Any, Callable, Dict + +class ProcessManager(object): + def __init__(self, func: Callable, kill_previous: Any = False, concurrent_running: Any = False) -> None: + """ProcessManager initializer + + Args: + func (Callable): function to be executed + kill_previous (Any, optional): Do you want to kill previous process? if not, new process won't be executed if concurrent is set to False. + If True, it will kill the unfinished previous process and start the new one. + Defaults to False. + concurrent_running (Any, optional): If True, all the process of the function will run concurrently. Defaults to False. + + Raises: + ValueError: kill_previous and concurrent_running can't be used together. If you kill previous, what do you wanna run concurrently? + """ + if concurrent_running and kill_previous: + raise ValueError("Using kill_previous is not allowed while using concurrent_running.") + self.func = func + self.kill_previous = kill_previous + self.concurrent_running = concurrent_running + + """ + We really don't need to keep track of multiple process. We will need that only when concurrent_running is true + and we don't need to terminate any process. So, no use of the process ids. + In the future, all the process management will be added if needed. + """ + self.process = None + + def run(self, kwargs: Dict = None) -> None: + """ create a new process of the function + + Args: + kwargs (Dict, optional): arguments to be passed to your function. Defaults to None. + """ + if self.concurrent_running == False and self.process is not None and self.process.is_alive(): + if self.kill_previous: + self.kill() + else: + return + + if kwargs == None: + self.process = multiprocessing.Process(target=self.func) + else: + self.process = multiprocessing.Process(target=self.func, kwargs=kwargs) + self.process.daemon = True + self.process.start() + + def kill(self) -> None: + """terminate the currently running process + """ + if self.process is not None and self.process.is_alive: + self.process.terminate() diff --git a/processpy/__init__.py b/processpy/__init__.py new file mode 100644 index 0000000..af54ca7 --- /dev/null +++ b/processpy/__init__.py @@ -0,0 +1,3 @@ +from .PManager import ProcessManager + +__all__ = ["ProcessManager"] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8bfead4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[tool.poetry] +name = "processpy" +version = "0.1.0" +description = "Python Process Manager" +authors = [ + "Nazmul Hasan " +] + +license = "MIT" +readme = "README.md" +repository = "https://github.com/nazmulnnb/processpy" + +classifiers = [ + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3 :: Only', +] + +[tool.poetry.dependencies] +python = "^3.6" + +[tool.poetry.dev-dependencies] +pytest = "^7.1" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d207934 --- /dev/null +++ b/readme.md @@ -0,0 +1,80 @@ +# Python Process Manager (processpy) + + +processpy is simple process manager for python. +If you want to run multiple process for the same function, this tool is for you. + +* You can run multiple process of the same function concurrently. +* You can choose to kill previous running process before running a new process of the same function. +* You can choose to ignore new process of the same function if it's already running. + + +## Installation + +```bash +pip install processpy +``` + +## Example (No concurrency and no previous kill) + +```python +from processpy import ProcessManager +import time + +def sum(a, b): + time.sleep(30) + print(a+b) + +sum_process = ProcessManager(sum, kill_previous=False, concurrent_running=False) +sum_process.run({'a': 10, 'b': 20}) +time.sleep(5) + +""" +The following will not run. Because concurrent run is false and kill previous is also false. So, it will simply return with doing nothing and let the previous run. +""" +sum_process.run({'a': 10, 'b': 20}) + +``` + +## Example (No concurrency but with previous kill) +```python +from processpy import ProcessManager +import time + +def sum(a, b): + time.sleep(30) + print(a+b) + +sum_process = ProcessManager(sum, kill_previous=True, concurrent_running=False) +sum_process.run({'a': 10, 'b': 20}) +time.sleep(5) + +""" +The following will kill the previous unfinished process and run. Because concurrent run is false and kill previous is True. So, it will simply kill the previous unfinished process. If previous one is already finished, nothing to kill. +""" +sum_process.run({'a': 10, 'b': 20}) +``` + +## Example (with concurrency) +```python +from processpy import ProcessManager +import time + +def sum(a, b): + time.sleep(30) + print(a+b) + +sum_process = ProcessManager(sum, concurrent_running=True) +sum_process.run({'a': 10, 'b': 20}) +time.sleep(5) + +""" +The following will run alongside of the previous process. +""" +sum_process.run({'a': 10, 'b': 20}) +``` + +## You can also kill the running process (if concurrent_running=False ) +```python +sub_process.kill() +``` \ No newline at end of file