#! /usr/bin/env python3
#
# Copyright 2018 California Institute of Technology
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ISOFIT: Imaging Spectrometer Optimal FITting
# Author: Philip G. Brodrick, philip.brodrick@jpl.nasa.gov
from collections import OrderedDict
from typing import Dict, List, Type
import numpy as np
import logging
[docs]class BaseConfigSection(object):
"""
Base Configuration Section from which all Configuration Sections inherit. Handles shared functionality like getting,
setting, and cleaning configuration options.
"""
def __init__(self) -> None:
return
[docs] def set_config_options(self, configdict: dict = None) -> None:
""" Read dictionary and assign to attributes, leaning on _set_callable_attributes
Args:
configdict: dictionary-style config for parsing
"""
for key in self._get_nontype_attributes():
keytype = getattr(self, '_' + key + '_type')
if key in configdict:
if callable(keytype):
sub_config = keytype(configdict[key])
setattr(self, key, sub_config)
else:
setattr(self, key, configdict[key])
return
[docs] def check_config_validity(self) -> List[str]:
errors = list()
message_type = (
"Invalid type for config option {} in config section {}. The provided value {} is a {}, "
+ "but the required value should be a {}."
)
# First check typing
for key in self._get_nontype_attributes():
# get the actual parameter value
value = getattr(self, key)
# None assignments are fine
if value is None:
continue
# check it against expected
type_expected = self._get_expected_type_for_option_key(key)
# Lists are complicated, retype
if isinstance(type_expected, List):
type_expected = List
if isinstance(value, type_expected):
continue
# At this point, we have a type mismatch, add to error list
errors.append(message_type.format(key, self.__class__.__name__,
value, type(value), type_expected))
errors.extend(self._check_config_validity())
# Now do a full check on each submodule
for key in self._get_nontype_attributes():
value = getattr(self, key)
try:
errors.extend(value.check_config_validity())
except AttributeError:
logging.debug('Configuration check: {} is not an object, skipping'.format(key))
return errors
[docs] def get_config_options_as_dict(self) -> Dict[str, Dict[str, any]]:
config_options = OrderedDict()
for key in self._get_nontype_attributes():
value = getattr(self, key)
if type(value) is tuple:
value = list(value) # Lists look nicer in config files and seem friendlier
config_options[key] = value
return config_options
def _check_config_validity(self) -> List[str]:
return list()
def _get_expected_type_for_option_key(self, option_key: str) -> type:
return getattr(self, "_{}_type".format(option_key))
def _get_nontype_attributes(self) -> List[str]:
keys = []
for key in self.__dict__.keys():
if key[0] == '_':
continue
keys.append(key)
return keys
def _get_type_attributes(self) -> List[str]:
keys = []
for key in self.__dict__.keys():
if key[0] == '_' and key[-5:] == '_type':
keys.append(key)
return keys
def _get_hidden_attributes(self) -> List[str]:
keys = []
for key in self.__dict__.keys():
if key[0] == '_' and key[-5:] != '_type':
keys.append(key)
return keys
[docs] def get_all_elements(self):
return [getattr(self, x) for x in self._get_nontype_attributes()]
[docs] def get_all_element_names(self):
return self._get_nontype_attributes()
[docs] def get_elements(self):
elements = self.get_all_elements()
element_names = self._get_nontype_attributes()
valid = [x is not None for x in elements]
elements = [elements[x] for x in range(len(elements)) if valid[x]]
element_names = [element_names[x] for x in range(len(element_names)) if valid[x]]
order = np.argsort(element_names)
elements = [elements[idx] for idx in order]
element_names = [element_names[idx] for idx in order]
return elements, element_names
[docs] def get_element_names(self):
elements, element_names = self.get_elements()
return element_names
[docs] def get_single_element_by_name(self, name):
elements, element_names = self.get_elements()
return elements[element_names.index(name)]