Source code for cosapp.core.config

"""
Configuration of CoSApp.
"""
import os
import sys
import json

try:
    from json import JSONDecodeError
except ImportError:
    JSONDecodeError = ValueError
import logging
from pathlib import Path
from typing import FrozenSet

import jsonschema

logger = logging.getLogger(__name__)


[docs]class CoSAppConfiguration: r"""Encapsulate CoSApp configuration parameters and its handlers. CoSApp configuration folder are by default store in On Linux:: $HOME/.cosapp.d On Windows:: %USERPROFILE%\.cosapp.d This default configuration folder is overwritten by the environment variable ``COSAPP_CONFIG_DIR``. """ COSAPP_CONFIG_DIR = ".cosapp.d" CONFIG_FILE = "cosapp_config.json" # type: str def __init__(self) -> None: """Constructor""" self._userid = "" # type: str self._roles = frozenset() # type: FrozenSet[FrozenSet[str]] self.__load_configuration() def __get_config_path(self) -> str: folder = Path( os.environ.get("COSAPP_CONFIG_DIR", None) or Path.home().joinpath(CoSAppConfiguration.COSAPP_CONFIG_DIR) ) return folder.joinpath(CoSAppConfiguration.CONFIG_FILE) def __load_configuration(self) -> None: """Read configuration from file or generate the default. Raises ------ OSError If the current platform is not recognized """ fullpath = self.__get_config_path() warning_msg = "The configuration file cannot be opened, fall back to default." if os.path.isfile(fullpath): # Load local parameters - offline connection try: parameters = self.validate_file(fullpath) self._userid = parameters["userid"] self._roles = frozenset( [frozenset(role) for role in parameters["roles"]] ) except (OSError, JSONDecodeError): logger.warning(warning_msg) else: logger.warning(warning_msg) self.update_configuration() # Try to update user permission from official root @property def userid(self) -> str: """str : User ID""" return self._userid @property def roles(self) -> FrozenSet[FrozenSet[str]]: """FrozenSet[FrozenSet[str]] : Roles that the user can impersonate.""" return self._roles
[docs] @staticmethod def validate_file(path: str) -> dict: """Validate the provided file against JSON schema for configuration file. Parameters ---------- path : str Absolute path to the file to be tested. Returns ------- dict The dictionary read in the validated file Raises ------ jsonschema.exceptions.ValidationError If the provided file does not conform to the JSON schema. """ with open( os.path.join( os.path.dirname(os.path.abspath(__file__)), "configuration_schema.json" ) ) as fp: config_schema = json.load(fp) with open(path) as untested_file: params = json.load(untested_file) jsonschema.validate(params, config_schema) return params
[docs] def update_configuration(self) -> None: """Update the configuration file for the current user. The update process ask the server about updated role. """ if len(self._userid) == 0: self._userid = os.environ.get("USERNAME", "") or os.environ.get("USER", "") if not self._userid: raise OSError("Unable to find the user id.") # TODO - request reference roles source here # Save back the changes following the update parameters = {"userid": self.userid, "roles": list()} for role in self.roles: parameters["roles"].append(list(role)) try: config_file = self.__get_config_path() config_dir = os.path.dirname(config_file) if not os.path.isdir(config_dir): os.makedirs(config_dir) with open(config_file, "w") as file: json.dump(parameters, file, sort_keys=True) except OSError: logger.warning("Fail to save configuration locally.")