Source code for inet_nm.config

"""Provide utilities for interacting with the inet_nm configuration.

It includes functionality for saving and loading information about
the boards and nodes, as well as for handling the path configuration.
"""

import argparse
import json
import os
from pathlib import Path
from typing import Dict, List, Union

from inet_nm._helpers import get_commit, nm_print
from inet_nm.data_types import EnvConfigFormat, NmNode


class _ConfigFile:
    _FILENAME = None
    _LOAD_TYPE = None

    def __init__(self, config_dir: Union[Path, str]):
        self.config_dir = Path(config_dir)
        self.file_path = Path(self.config_dir / self._FILENAME).expanduser()

    def check_file(self, writable: bool = False) -> bool:
        """
        Check if a file exists and can be accessed.

        Args:
            writable: If True, check if the file is writable.

        Returns:
            True if the file exists and can be accessed, False otherwise.
        """
        file_path = self.file_path
        file_path.parent.mkdir(parents=True, exist_ok=True)

        if writable:
            file_path.touch()
        else:
            if not file_path.exists():
                return False
            if not file_path.stat().st_size:
                return False
            with file_path.open():
                pass
        return True

    def save(self, data):
        self.check_file(writable=True)
        with self.file_path.open("w") as file:
            json.dump(data, file, indent=2, sort_keys=True)

    def load(self):
        if not self.check_file(writable=False):
            return self._LOAD_TYPE()
        with self.file_path.open() as file:
            return json.load(file)


[docs] class BoardInfoConfig(_ConfigFile): """Class for handling the board info configuration. The board info configuration is a JSON file containing a dictionary with the board name as key and a list of features provided by the board as value. The board info configuration file is located in the config directory and is named board_info.json. Args: config_dir: Directory for the configuration files. Attributes: file_path (Path): Path to the board info configuration file. """ _FILENAME = "board_info.json" _LOAD_TYPE = dict
[docs] def save(self, data: Dict[str, Union[str, int]]): """Save the board info configuration. Args: data: The board info data to save. """ return super().save(data)
[docs] def load(self) -> Dict[str, Union[str, int]]: """Load the board info configuration. Returns: The loaded board info data. """ data = super().load() user_path = Path(self.file_path.parent / f"user_{self._FILENAME}") try: with user_path.open() as file: user_data = json.load(file) # Extend data with user list of features for board, features in user_data.items(): if board not in data: data[board] = [] data[board].extend(features) except (FileNotFoundError, json.decoder.JSONDecodeError): pass return data
[docs] class BoardInfoCommitHash(_ConfigFile): """Class for handling the board info configuration hash. This is just the git commit used for the board info generation. """ _FILENAME = "board_info_commit_hash.json" _LOAD_TYPE = list
[docs] def save(self, data: str): """Save the board info commit hash. Args: data: The board info data to save. """ return super().save(data)
[docs] def load(self) -> List[str]: """Load the board info commit hash. Returns: The board info commit hash. """ return super().load()
[docs] class NodesConfig(_ConfigFile): """Class for handling the nodes configuration. The nodes configuration is a JSON file containing a list of NmNode objects. The nodes configuration file is located in the config directory and is named nodes.json. Args: config_dir: Directory for the configuration files. Attributes: file_path (Path): Path to the nodes configuration file. """ _FILENAME = "nodes.json" _LOAD_TYPE = list
[docs] def save(self, data: List[NmNode]): """Save the nodes configuration. Args: data: The nodes data to save. """ data = [node.to_dict() for node in data] node: dict for node in data: if "features_provided" in node: node["features_provided"] = [] return super().save(data)
[docs] def load(self) -> List[NmNode]: """Load the nodes configuration. Returns: The loaded nodes data. """ data = super().load() nodes = [NmNode.from_dict(item) for item in data] user_path = Path(self.file_path.parent / "user_node_info.json") try: with user_path.open() as file: user_data = json.load(file) for uid, features in user_data.items(): node: NmNode for node in nodes: if node.uid == uid: node.features_provided.extend(features) break except (FileNotFoundError, json.decoder.JSONDecodeError): pass # Directly add features provided from the board info bic = BoardInfoConfig(self.config_dir) bi = bic.load() for node in nodes: if node.board in bi: fp = set((bi[node.board] or []) + (node.features_provided or [])) node.features_provided = sorted(list(fp)) return nodes
[docs] class EnvConfig(_ConfigFile): """Class for handling the environment configuration. The env configuration is a JSON file containing a env vars. The env configuration file is located in the config directory and is named env.json. Args: config_dir: Directory for the configuration files. Attributes: file_path (Path): Path to the env configuration file. """ _FILENAME = "env.json" _LOAD_TYPE = EnvConfigFormat
[docs] def save(self, data: EnvConfigFormat): """Save the env configuration. Args: data: The env data to save. """ return super().save(data.to_dict())
[docs] def load(self) -> EnvConfigFormat: """Load the env configuration. Returns: The loaded env data. """ empty = EnvConfigFormat(nodes={}, patterns=[], shared={}) if not self.check_file(writable=False): return empty data = super().load() if not data: return empty return EnvConfigFormat.from_dict(data)
[docs] class LocationConfig(_ConfigFile): """Class for handling the location mapping. Args: config_dir: Directory for the configuration files. Attributes: file_path (Path): Path to the env configuration file. """ _FILENAME = "location.json" _LOAD_TYPE = dict
[docs] class LocationCache(_ConfigFile): """Class for handling the location cache. Args: config_dir: Directory for the configuration files. Attributes: file_path (Path): Path to the env configuration file. """ _FILENAME = "location_cache.json" _LOAD_TYPE = list
[docs] def get_default_path() -> Path: """ Return the default path for the configuration files. Returns: Path: The default path for the configuration files. """ return Path(os.environ.get("NM_CONFIG_DIR", "~/.config/inet_nm")).expanduser()
[docs] def config_arg(parser: argparse.ArgumentParser): """ Add a configuration argument to the provided parser. Args: parser (argparse.ArgumentParser): ArgumentParser object. """ parser.add_argument( "-c", "--config", default=get_default_path(), help="Path to the config dir, defaults to NM_CONFIG_DIR or " "~/.config/inet_nm if NM_CONFIG_DIR is not set", )
[docs] def check_commit_hash(config_dir: Union[Path, str]) -> bool: """ Check if the commit hash is the same as the current commit hash. Args: config_dir: Directory for the configuration files. Returns: True if the commit hash is the same as the current commit hash, False otherwise. """ bich = BoardInfoCommitHash(config_dir) commit_data = bich.load() current_commit_data = get_commit() if current_commit_data and commit_data: if current_commit_data[1] != commit_data[1]: nm_print( "WARNING: The board info commit hash has changed \n" "Please run 'inet-nm update-from-os' to update the board info." ) return False return True