Source code for perun.monitoring.application
"""Application module."""
import gc
import logging
import logging.handlers
import os
import subprocess
from configparser import ConfigParser
from pathlib import Path
from typing import Any, Callable
log = logging.getLogger(__name__)
[docs]
class Application:
"""
Represents an application to be executed.
Parameters
----------
app : Path | Callable
The application to be executed. It can be either a file path or a callable object.
config : ConfigParser
The configuration object containing application settings.
args : tuple, optional
Positional arguments to be passed to the application (default is an empty tuple).
kwargs : dict, optional
Keyword arguments to be passed to the application (default is an empty dictionary).
Attributes
----------
name : str
The name of the application.
args : tuple
The positional arguments to be passed to the application.
kwargs : dict
The keyword arguments to be passed to the application.
Methods
-------
run()
Executes the application.
"""
def __init__(
self,
app: Path | Callable | str,
config: ConfigParser,
is_binary: bool = False,
args: tuple[Any, ...] = (),
kwargs: dict[str, Any] = {},
):
self._app = app
self._name = self._setName(config)
self._args = args
self._kwargs = kwargs
self._is_binary = is_binary
if isinstance(app, Path):
try:
with open(app, "r") as scriptFile:
self._scriptFile = scriptFile.read()
except FileNotFoundError:
log.error(
f"perun could not find the file {app}. Please check the path."
)
exit()
@property
def name(self) -> str:
"""Return the application name."""
return self._name
@property
def args(self) -> tuple[Any, ...]:
"""Return the application positional arguments."""
return self._args
@property
def kwargs(self) -> dict[str, Any]:
"""Return the application keyword arguments."""
return self._kwargs
@property
def is_binary(self) -> bool:
"""Return the application keyword arguments."""
return self._is_binary
def _setName(self, config: ConfigParser) -> str:
"""
Return the application name based on the configuration and application path.
Parameters
----------
config : ConfigParser
The configuration object containing application settings.
Returns
-------
str
The application name.
"""
app_name = config.get("output", "app_name")
if app_name and app_name != "SLURM":
return app_name
elif (
app_name
and app_name != "SLURM"
and "SBATCH_JOB_NAME" in os.environ
and app_name == "SLURM"
):
return os.environ["SBATCH_JOB_NAME"]
elif isinstance(self._app, Path):
return self._app.stem
elif callable(self._app):
return self._app.__name__
elif isinstance(self._app, str):
return self._app
else:
raise ValueError("Application name not found")
def _cleanup(self) -> None:
for i in range(3):
gc.collect(i)
[docs]
def run(self) -> Any:
"""
Execute the application. If callable, returns the function result.
Raises
------
ValueError
If the application is not found.
"""
if self._is_binary and isinstance(self._app, str):
subprocess.run([self._app, *self._args], env=os.environ)
elif isinstance(self._app, Path):
exec(
self._scriptFile,
{"__name__": "__main__", "__file__": self.name},
)
self._cleanup()
elif callable(self._app):
result = self._app(*self._args, **self._kwargs)
self._cleanup()
return result
else:
raise ValueError("Application not found")
def __str__(self) -> str:
"""Return the application name."""
return f"{self.name}"
def __repr__(self) -> str:
"""Return the application name."""
return f"Application({self.name})"