Source code for nxtomomill.io.config.configbase

# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
#
# ###########################################################################


__authors__ = [
    "H. Payno",
]
__license__ = "MIT"
__date__ = "21/04/2022"


import configparser
import logging
from typing import Iterable, Union

from nxtomo.nxobject.nxdetector import FieldOfView
from nxtomomill.utils import FileExtension


[docs]class ConfigBase: __isfrozen = False # to ease API and avoid setting wrong attributes we 'freeze' the attributes # see https://stackoverflow.com/questions/3603502/prevent-creating-new-attributes-outside-init
[docs] def __init__(self) -> None: self._output_file = None self._overwrite = False self._file_extension = FileExtension.NX self._log_level = logging.WARNING self._field_of_view = None self._machine_electric_current_keys = None
def __setattr__(self, __name, __value): if self.__isfrozen and not hasattr(self, __name): raise AttributeError("can't set attribute", __name) else: super().__setattr__(__name, __value) @property def output_file(self) -> Union[None, str]: return self._output_file @output_file.setter def output_file(self, output_file: Union[None, str]): if not isinstance(output_file, (str, type(None))): raise TypeError("'input_file' should be None or an instance of Iterable") elif output_file == "": self._output_file = None else: self._output_file = output_file @property def overwrite(self) -> bool: return self._overwrite @overwrite.setter def overwrite(self, overwrite: bool) -> None: if not isinstance(overwrite, bool): raise TypeError("'overwrite' should be a boolean") else: self._overwrite = overwrite @property def file_extension(self) -> FileExtension: return self._file_extension @file_extension.setter def file_extension(self, file_extension: str): self._file_extension = FileExtension.from_value(file_extension) @property def log_level(self): return self._log_level @log_level.setter def log_level(self, level: str): self._log_level = getattr(logging, level.upper()) def _set_freeze(self, freeze=True): self.__isfrozen = freeze @property def field_of_view(self) -> Union[None, FieldOfView]: return self._field_of_view @field_of_view.setter def field_of_view(self, fov: Union[None, FieldOfView, str]): if fov is None: self._field_of_view = fov elif isinstance(fov, str): self._field_of_view = FieldOfView.from_value(fov.title()) elif isinstance(fov, FieldOfView): self._field_of_view = fov else: raise TypeError( f"fov is expected to be None, a string or FieldOfView. Not {type(fov)}" ) @property def rotation_angle_keys(self) -> Iterable: return self._rot_angle_keys @rotation_angle_keys.setter def rotation_angle_keys(self, keys: Iterable): if not isinstance(keys, Iterable) or isinstance(keys, str): raise TypeError("'keys' should be an Iterable") else: for elmt in keys: if not isinstance(elmt, str): raise TypeError("keys elmts are expected to be str") self._rot_angle_keys = keys @property def x_trans_keys(self) -> Iterable: return self._x_trans_keys @x_trans_keys.setter def x_trans_keys(self, keys) -> None: if not isinstance(keys, Iterable) or isinstance(keys, str): raise TypeError("'keys' should be an Iterable") else: for elmt in keys: if not isinstance(elmt, str): raise TypeError("keys elmts are expected to be str") self._x_trans_keys = keys @property def y_trans_keys(self) -> Iterable: return self._y_trans_keys @y_trans_keys.setter def y_trans_keys(self, keys) -> None: if not isinstance(keys, Iterable) or isinstance(keys, str): raise TypeError("'keys' should be an Iterable") else: for elmt in keys: if not isinstance(elmt, str): raise TypeError("keys elmts are expected to be str") self._y_trans_keys = keys @property def z_trans_keys(self) -> Iterable: return self._z_trans_keys @z_trans_keys.setter def z_trans_keys(self, keys) -> None: if not isinstance(keys, Iterable) or isinstance(keys, str): raise TypeError("'keys' should be an Iterable") else: for elmt in keys: if not isinstance(elmt, str): raise TypeError("keys elmts are expected to be str") self._z_trans_keys = keys @property def machine_electric_current_keys(self) -> Iterable: return self._machine_electric_current_keys @machine_electric_current_keys.setter def machine_electric_current_keys(self, keys: Iterable) -> None: self._machine_electric_current_keys = keys
[docs] def to_dict(self) -> dict: """convert the configuration to a dictionary""" raise NotImplementedError("Base class")
[docs] def load_from_dict(self, dict_: dict) -> None: """Load the configuration from a dictionary""" raise NotImplementedError("Base class")
@staticmethod def from_dict(dict_: dict): raise NotImplementedError("Base class") def to_cfg_file(self, file_path: str): # TODO: add some generic information like:provided order of the tuple # will be the effective one. You can provide a key from it names if # it is contained in the positioners group # maybe split in sub section ? self.dict_to_cfg(file_path=file_path, dict_=self.to_dict()) @staticmethod def dict_to_cfg(file_path, dict_): """ """ raise NotImplementedError("Base class") @staticmethod def _dict_to_cfg(file_path, dict_, comments_fct, logger): """ """ if not file_path.lower().endswith((".cfg", ".config", ".conf")): logger.warning("add a valid extension to the output file") file_path += ".cfg" config = configparser.ConfigParser(allow_no_value=True) config.optionxform = str for section_name, values in dict_.items(): config.add_section(section_name) config.set(section_name, "# " + comments_fct(section_name), None) for key, value in values.items(): # adopt nabu design: comments are set prior to the key config.set(section_name, "# " + comments_fct(key), None) config.set(section_name, key, str(value)) with open(file_path, "w") as config_file: config.write(config_file) @staticmethod def from_cfg_file(file_path: str, encoding=None): raise NotImplementedError("Base class") @staticmethod def get_comments(key): raise NotImplementedError("Base class") def __str__(self): return str(self.to_dict())