Source code for psctb.utils.config

#from __future__ import division, print_function
#from __future__ import absolute_import
#from __future__ import unicode_literals

from configparser import ConfigParser
from sys import platform, stdout
from os import path, sep, listdir, access, X_OK, environ, pathsep
# from pkg_resources import resource_stream, resource_filename
from pysces import output_dir
from warnings import warn

_DEFAULT_CONFIG = {'Settings': {
    'maxima_path': 'C:\\maxima?\\bin\\maxima.bat'}}

_DEFAULT_CONF_NAME = 'default_config.ini'
_USER_CONF_PATH = path.join(output_dir, 'psctb_config.ini')


[docs]class ConfigReader: _config = None
[docs] @classmethod def reload_config(cls): cls._config = None
[docs] @classmethod def get_config(cls): if not cls._config: cls._setup_config() return cls._config
@classmethod def _setup_config(cls): default_conf = ConfigParser() default_conf._sections = _DEFAULT_CONFIG try: if not path.exists(_USER_CONF_PATH): ConfigWriter.write_config(_DEFAULT_CONFIG, _USER_CONF_PATH) user_conf = ConfigParser() user_conf._sections = _DEFAULT_CONFIG else: user_conf = cls._read_config(_USER_CONF_PATH) ConfigChecker.check_config('user configuration', _USER_CONF_PATH, user_conf._sections) except MissingSection as e: solution = ('falling back to default configuration.' '\nDelete configuration file to restore defaults') ConfigChecker.warn_user(e, solution) user_conf = ConfigParser() user_conf.add_section('Settings') except MissingSetting as e: solution = ('falling back to default configuration.' '\nSpecify setting to avoid using defaults') ConfigChecker.warn_user(e, solution) cls._config = cls._compose_config(default_conf, user_conf) try: if platform == 'win32': maxima_path_list = PathFinder.find_path_to( cls._config['maxima_path']) if not len(maxima_path_list) > 0: raise IOError(2, 'No valid path to specified file', cls._config['maxima_path']) maxima_path = sorted(maxima_path_list)[-1] cls._config['maxima_path'] = maxima_path else: maxima_path = PathFinder.which('maxima') if not maxima_path: raise IOError(2, 'Maxima not installed', 'command not found') cls._config['maxima_path'] = 'maxima' except IOError as e: solution = ('Please check that configuration file specifies ' 'the correct path for Maxima and ' 'that Maxima is installed correctly before ' 'attempting to generate new results with SymCA ' '(see documentation for details).') ConfigChecker.warn_user(e, solution) cls._config['maxima_path'] = None cls._config['platform'] = platform cls._config['stdout'] = stdout @staticmethod def _read_config(file_or_path): conf = ConfigParser() if type(file_or_path) is str: conf.read(file_or_path) else: conf.readfp(file_or_path) return conf @staticmethod def _compose_config(default_conf, user_conf): conf_dict = {} conf_dict.update(default_conf._sections['Settings']) conf_dict.update(user_conf._sections['Settings']) if '__name__' in conf_dict: conf_dict.pop('__name__') return conf_dict
[docs]class ConfigChecker: @staticmethod def _has_all_sections(config_name, config_path, config_dict): error_string = ('The {config_name} located at\n{config_path}\n' 'does not contain the required section\n' '"{section}".') for section in list(_DEFAULT_CONFIG.keys()): if section not in config_dict: raise MissingSection( error_string.format(**locals())) @staticmethod def _has_all_settings(config_name, config_path, config_dict): error_string = ('The {config_name} located at\n{config_path}\n' 'does not contain the required setting\n' '"{setting}"\nunder the section\n"{section}".') for section in list(_DEFAULT_CONFIG.keys()): for setting in list(_DEFAULT_CONFIG[section].keys()): if setting not in config_dict[section]: raise MissingSetting( error_string.format(**locals()))
[docs] @staticmethod def check_config(config_name, config_path, config_dict): ConfigChecker._has_all_sections(config_name, config_path, config_dict) ConfigChecker._has_all_settings(config_name, config_path, config_dict)
@staticmethod def _get_exception_name(exception): class_name = str(exception.__class__) return class_name[:-2].split('.')[-1]
[docs] @staticmethod def warn_user(exception, solution): warning_string = ('\n\n' 'The following error was encountered:\n"{message}"' '\n\n{solution}') if type(exception) is IOError: exception.message = exception.strerror + ':\n' + exception.filename message = ConfigChecker._get_exception_name(exception) + \ ' - ' + exception.message warn(warning_string.format(**locals()))
[docs]class ConfigWriter:
[docs] @staticmethod def write_config(config_dict, config_path): conf = ConfigParser() for section, settings in config_dict.items(): conf.add_section(section) for setting, value in settings.items(): conf.set(section, setting, value) with open(config_path, 'w') as f: conf.write(f)
[docs]class MissingSetting(Exception): pass
[docs]class MissingSection(Exception): pass
[docs]class PathFinder:
[docs] @staticmethod def find_path_to(wildcard_path): path_parts = wildcard_path.split(sep) if platform == 'win32': new_paths = [path_parts.pop(0) + sep] else: new_paths = [sep] for i, path_part in enumerate(path_parts): if '?' in path_part: path_part = path_part[:-1] new_new_paths = [] for each_base in new_paths: possible_matches = PathFinder.find_match(each_base, path_part) if len(possible_matches) > 0: for match in possible_matches: new_new_paths.append(path.join(each_base, match)) new_paths = new_new_paths else: new_new_paths = [] for each_path in new_paths: new_path = path.join(each_path, path_part) if path.exists(new_path): new_new_paths.append(new_path) new_paths = new_new_paths return new_paths
[docs] @staticmethod def find_match(base_dir, to_match): matches = [] try: dirs_in_basedir = listdir(base_dir) except OSError: dirs_in_basedir = [] for directory in dirs_in_basedir: if to_match in directory: matches.append(directory) return matches
[docs] @staticmethod def which(program): def is_exe(fpath): return path.isfile(fpath) and access(fpath, X_OK) fpath, fname = path.split(program) if fpath: if is_exe(program): return program else: for epath in environ["PATH"].split(pathsep): epath = epath.strip('"') exe_file = path.join(epath, program) if is_exe(exe_file): return exe_file return None