From 163e90eae896ecc353bc678a8501a92220742b1c Mon Sep 17 00:00:00 2001 From: Emily Hunt Date: Thu, 7 Dec 2023 18:46:33 +0100 Subject: [PATCH] Start adding interfaces for various actions --- src/smartmultiprocessing/config.py | 48 +++++++++++++++++++++ src/smartmultiprocessing/gui/__init__.py | 1 + src/smartmultiprocessing/gui/base.py | 26 +++++++++++ src/smartmultiprocessing/gui/commands.py | 34 +++++++++++++++ src/smartmultiprocessing/gui/interactive.py | 6 +++ src/smartmultiprocessing/gui/printer.py | 29 +++++++++++++ src/smartmultiprocessing/gui/terminal.py | 6 +++ src/smartmultiprocessing/gui/updates.py | 8 ++++ src/smartmultiprocessing/manager.py | 6 +++ 9 files changed, 164 insertions(+) create mode 100644 src/smartmultiprocessing/config.py create mode 100644 src/smartmultiprocessing/gui/__init__.py create mode 100644 src/smartmultiprocessing/gui/base.py create mode 100644 src/smartmultiprocessing/gui/commands.py create mode 100644 src/smartmultiprocessing/gui/interactive.py create mode 100644 src/smartmultiprocessing/gui/printer.py create mode 100644 src/smartmultiprocessing/gui/terminal.py create mode 100644 src/smartmultiprocessing/gui/updates.py create mode 100644 src/smartmultiprocessing/manager.py diff --git a/src/smartmultiprocessing/config.py b/src/smartmultiprocessing/config.py new file mode 100644 index 0000000..5b2a8ff --- /dev/null +++ b/src/smartmultiprocessing/config.py @@ -0,0 +1,48 @@ +from ctypes import Union +from dataclasses import dataclass +from pathlib import Path +from typing import Optional +import psutil +from smartmultiprocessing.gui.base import SmartMultiprocessingGui + +from smartmultiprocessing.gui.printer import PrintGUI + + +def usable_physical_cpu_count(): + physical_cpu_count = psutil.cpu_count(logical=False) + usable_cpu_fraction = psutil.cpu_count(logical=True) / len( + psutil.Process().cpu_affinity() + ) + usable_cpu_count = int(physical_cpu_count * usable_cpu_fraction) + if usable_cpu_count < 1: + raise RuntimeError("CPU count could not be automatically estimated.") + return usable_cpu_count + + +def total_system_memory(): + return psutil.virtual_memory().total + + +PHYSICAL_CORE_COUNT = usable_physical_cpu_count() +TOTAL_PHYSICAL_MEMORY = total_system_memory() +DEFAULT_MEMORY_PER_PROCESS = int(TOTAL_PHYSICAL_MEMORY / PHYSICAL_CORE_COUNT * 0.7) + + +@dataclass +class Config: + # Metadata + name: str = "Untitled_SmartMultiprocessing_Run" + gui: Optional[SmartMultiprocessingGui] = PrintGUI + + # How processes are handled + process_count: int = PHYSICAL_CORE_COUNT + memory: int = int(0.6 * TOTAL_PHYSICAL_MEMORY) + max_memory: int = int(0.8 * TOTAL_PHYSICAL_MEMORY) + error_on_overmemory: bool = False + stop_all_on_error: bool = True + failed_task_repeats: int = 0 + + # Logging information + log_main_thread: bool = False + logging_directory: Union[str, Path] = Path("./logs") + log_every_process: bool = False diff --git a/src/smartmultiprocessing/gui/__init__.py b/src/smartmultiprocessing/gui/__init__.py new file mode 100644 index 0000000..a64fa2d --- /dev/null +++ b/src/smartmultiprocessing/gui/__init__.py @@ -0,0 +1 @@ +from .printer import PrintGUI diff --git a/src/smartmultiprocessing/gui/base.py b/src/smartmultiprocessing/gui/base.py new file mode 100644 index 0000000..bc06c7c --- /dev/null +++ b/src/smartmultiprocessing/gui/base.py @@ -0,0 +1,26 @@ +from abc import ABCMeta, abstractmethod +from typing import Iterable, Optional +from smartmultiprocessing.gui.commands import SmartMultiprocessingCommand +from smartmultiprocessing.gui.updates import SmartMultiprocessingGuiUpdate + +class SmartMultiprocessingGui(metaclass=ABCMeta): + @abstractmethod + def start(self): + """Starts the GUI.""" + pass + + @abstractmethod + def stop(self): + """Stops the GUI.""" + pass + + @abstractmethod + def update(self, updates: Iterable[SmartMultiprocessingGuiUpdate]): + """Updates the current state of the GUI.""" + pass + + @abstractmethod + def get_commands(self) -> Optional[Iterable[SmartMultiprocessingCommand]]: + """Checks to see if there is any current user input. If yes, returns an iterable + list of commands; if no, returns None.""" + pass diff --git a/src/smartmultiprocessing/gui/commands.py b/src/smartmultiprocessing/gui/commands.py new file mode 100644 index 0000000..2c2ee10 --- /dev/null +++ b/src/smartmultiprocessing/gui/commands.py @@ -0,0 +1,34 @@ +from abc import ABC +from typing import Any, Mapping + + +class SmartMultiprocessingCommand(ABC): + pass + + +class StopCommand(SmartMultiprocessingCommand): + def __init__(self) -> None: + """Asks a system to stop after execution of all current processes.""" + raise NotImplementedError("Command not implemented.") + + +class TerminateCommand(SmartMultiprocessingCommand): + def __init__(self) -> None: + """Politely terminates a system (SIGTERM).""" + raise NotImplementedError("Command not implemented.") + + +class KillCommand(SmartMultiprocessingCommand): + def __init__(self) -> None: + """Ask a system to stop immediately (SIGKILL).""" + raise NotImplementedError("Command not implemented.") + + +class UpdateCommand(SmartMultiprocessingCommand): + def __init__(self) -> None: + """Updates some arbitrary aspect of the configuration of the system.""" + raise NotImplementedError("Command not implemented.") + + def get_update(self) -> Mapping[str, Any]: + """Get the update to be applied to the system.""" + pass diff --git a/src/smartmultiprocessing/gui/interactive.py b/src/smartmultiprocessing/gui/interactive.py new file mode 100644 index 0000000..462cb8d --- /dev/null +++ b/src/smartmultiprocessing/gui/interactive.py @@ -0,0 +1,6 @@ +from smartmultiprocessing.gui.base import SmartMultiprocessingGui + + +class InteractiveModeGUI(SmartMultiprocessingGui): + def __init__(self) -> None: + raise NotImplementedError("GUI type not implemented.") diff --git a/src/smartmultiprocessing/gui/printer.py b/src/smartmultiprocessing/gui/printer.py new file mode 100644 index 0000000..5ca6a9c --- /dev/null +++ b/src/smartmultiprocessing/gui/printer.py @@ -0,0 +1,29 @@ +from typing import Iterable +from smartmultiprocessing import utilities +from smartmultiprocessing.gui.base import SmartMultiprocessingGui +from smartmultiprocessing.gui.updates import SmartMultiprocessingGuiUpdate + + +class PrintGUI(SmartMultiprocessingGui): + def __init__(self) -> None: + """The most basic possible SmartMultiprocessing GUI! It simply prints to + console. + + You should probably check out the more advanced GUIs implemented in the library; + this one is only really intended for testing purposes, or systems with very + limited resources. + """ + pass + + def start(self): + pass + + def stop(self): + pass + + def update(self, updates: Iterable[SmartMultiprocessingGuiUpdate]): + for update in updates: + print(utilities.timestamp() + update.to_string()) + + def get_commands(self): + return None diff --git a/src/smartmultiprocessing/gui/terminal.py b/src/smartmultiprocessing/gui/terminal.py new file mode 100644 index 0000000..da79982 --- /dev/null +++ b/src/smartmultiprocessing/gui/terminal.py @@ -0,0 +1,6 @@ +from smartmultiprocessing.gui.base import SmartMultiprocessingGui + + +class TerminalGUI(SmartMultiprocessingGui): + def __init__(self) -> None: + raise NotImplementedError("GUI type not implemented.") diff --git a/src/smartmultiprocessing/gui/updates.py b/src/smartmultiprocessing/gui/updates.py new file mode 100644 index 0000000..e0a5d4b --- /dev/null +++ b/src/smartmultiprocessing/gui/updates.py @@ -0,0 +1,8 @@ +from abc import ABC, abstractmethod + + +class SmartMultiprocessingGuiUpdate(ABC): + @abstractmethod + def to_string(self): + """Converts a GUI update to a string.""" + pass diff --git a/src/smartmultiprocessing/manager.py b/src/smartmultiprocessing/manager.py new file mode 100644 index 0000000..0505bfa --- /dev/null +++ b/src/smartmultiprocessing/manager.py @@ -0,0 +1,6 @@ +class ProcessManager: + def __init__(self, config) -> None: + """Basic process manager shared amongst all high-level APIs. Not intended for + use by users; instead, please use SmartQueue and SmartPool objects. + """ + pass \ No newline at end of file