Source code for dependencies
"""
Simtools dependencies version management.
This modules provides two main functionalities:
- retrieve the versions of simtools dependencies (e.g., databases, sim_telarray, CORSIKA)
- provide space for future implementations of version management
"""
import logging
import os
import re
import subprocess
from pathlib import Path
import simtools.utils.general as gen
from simtools.db.db_handler import DatabaseHandler
_logger = logging.getLogger(__name__)
[docs]
def get_version_string(db_config=None):
"""Print the versions of the dependencies."""
return (
f"Database version: {get_database_version(db_config)}\n"
f"sim_telarray version: {get_sim_telarray_version()}\n"
f"CORSIKA version: {get_corsika_version()}\n"
)
[docs]
def get_database_version(db_config):
"""
Get the version of the simulation model data base used.
Parameters
----------
db_config : dict
Dictionary containing the database configuration.
Returns
-------
str
Version of the simulation model data base used.
"""
if db_config is None:
return None
db = DatabaseHandler(db_config)
return db.mongo_db_config.get("db_simulation_model")
[docs]
def get_sim_telarray_version():
"""
Get the version of the sim_telarray package using 'sim_telarray --version'.
Returns
-------
str
Version of the sim_telarray package.
"""
sim_telarray_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
if sim_telarray_path is None:
_logger.warning("Environment variable SIMTOOLS_SIMTEL_PATH is not set.")
return None
sim_telarray_path = Path(sim_telarray_path) / "sim_telarray" / "bin" / "sim_telarray"
# expect stdout with e.g. a line 'Release: 2024.271.0 from 2024-09-27'
result = subprocess.run(
[sim_telarray_path, "--version"],
capture_output=True,
text=True,
check=False,
)
match = re.search(r"^Release:\s+(.+)", result.stdout, re.MULTILINE)
if match:
return match.group(1).split()[0]
raise ValueError(f"sim_telarray release not found in {result.stdout}")
[docs]
def get_corsika_version():
"""
Get the version of the CORSIKA package.
Returns
-------
str
Version of the CORSIKA package.
"""
version = None
sim_telarray_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
if sim_telarray_path is None:
_logger.warning("Environment variable SIMTOOLS_SIMTEL_PATH is not set.")
return None
corsika_command = Path(sim_telarray_path) / "corsika-run" / "corsika"
# Below I do not use the standard context manager because
# it makes mocking in the tests significantly more difficult
process = subprocess.Popen( # pylint: disable=consider-using-with
corsika_command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
text=True,
)
# Capture output until it waits for input
while True:
line = process.stdout.readline()
if not line:
break
# Extract the version from the line "NUMBER OF VERSION : 7.7550"
if "NUMBER OF VERSION" in line:
version = line.split(":")[1].strip()
break
# Check for a specific prompt or indication that the program is waiting for input
if "DATA CARDS FOR RUN STEERING ARE EXPECTED FROM STANDARD INPUT" in line:
break
process.terminate()
# Check it's a valid version string
if version and re.match(r"\d+\.\d+", version):
return version
try:
build_opts = get_build_options()
except (FileNotFoundError, TypeError):
_logger.warning("Could not get CORSIKA version.")
return None
_logger.debug("Getting the CORSIKA version from the build options.")
return build_opts.get("corsika_version")
[docs]
def get_build_options():
"""
Return CORSIKA / sim_telarray build options.
Expects a build_opts.yml file in the sim_telarray directory.
"""
try:
return gen.collect_data_from_file(
Path(os.getenv("SIMTOOLS_SIMTEL_PATH")) / "build_opts.yml"
)
except FileNotFoundError as exc:
raise FileNotFoundError("No build_opts.yml file found.") from exc
except TypeError as exc:
raise TypeError("SIMTOOLS_SIMTEL_PATH not defined.") from exc