diff --git a/bsp/__init__.py b/bsp/__init__.py index afd809c4..62925462 100644 --- a/bsp/__init__.py +++ b/bsp/__init__.py @@ -27,6 +27,7 @@ ] VERBOSE_REGISTRATION = False +ATTEMPT_INSTALL = False # process/implementation registrar @@ -50,7 +51,8 @@ app_registrar.register_initial_modules( items_to_register=items_to_register, - verbose=VERBOSE_REGISTRATION + verbose=VERBOSE_REGISTRATION, + attempt_install=ATTEMPT_INSTALL ) diff --git a/bsp/registration.py b/bsp/registration.py index 64fe11ea..4f78b01c 100644 --- a/bsp/registration.py +++ b/bsp/registration.py @@ -4,6 +4,8 @@ from process_bigraph import ProcessTypes +from bsp.utils.base_utils import dynamic_simulator_install + @dataclasses.dataclass class ImplementationRegistry: @@ -65,7 +67,7 @@ def register_process(self, address: str, implementation: object, verbose=False) if verbose: print(f"Successfully registered {implementation} to address: {address}") - def register_module(self, process_name: str, path: str, package: str = "bsp", verbose=False) -> None: + def register_module(self, process_name: str, path: str, package: str = "bsp", verbose=False, attempt_install=False) -> None: library, module_name, class_name = path.rsplit('.', 3) try: # library = 'steps' if 'process' not in path else 'processes' @@ -77,9 +79,11 @@ def register_module(self, process_name: str, path: str, package: str = "bsp", ve except Exception as e: if verbose: print(f"Cannot register {class_name}. Error:\n**\n{e}\n**") + if attempt_install: + dynamic_simulator_install(simulators=[library]) - def register_initial_modules(self, items_to_register: List[Tuple[str, str]], package: str = "bsp", verbose=False) -> None: + def register_initial_modules(self, items_to_register: List[Tuple[str, str]], package: str = "bsp", verbose=False, attempt_install=False) -> None: if not self.initial_registration_complete: for process_name, path in items_to_register: - self.register_module(process_name, path, package, verbose) + self.register_module(process_name=process_name, path=path, package=package, verbose=verbose, attempt_install=attempt_install) self.initial_registration_complete = True diff --git a/bsp/utils/base_utils.py b/bsp/utils/base_utils.py index c607f403..d7c62f09 100644 --- a/bsp/utils/base_utils.py +++ b/bsp/utils/base_utils.py @@ -1,4 +1,7 @@ import traceback +import subprocess +import sys +import importlib from dataclasses import dataclass from pprint import pformat from types import ModuleType @@ -39,3 +42,21 @@ def dynamic_simulator_import(module_name: str) -> SimulatorImport: return SimulatorImport(simulator_id=module_name, module=module) +def dynamic_simulator_install(simulators: list[str], verbose: bool = True): + """ + Dynamically installs required simulator libraries. + + :param simulators: (`list[str]`) list of simulator libraries to install + :param verbose: (`bool`) whether to print progress confirmations + """ + for sim in simulators: + try: + # Check if the simulator is already installed + importlib.import_module(sim) + print(f"{sim} is already installed.") if verbose else None + except ImportError: + # Install using pip in the current environment + print(f"Installing {sim}...") if verbose else None + subprocess.check_call([sys.executable, "-m", "pip", "install", sim]) + print(f"{sim} installed successfully.") if verbose else None +