Source code for nxtomomill.io.config.configbase

# coding: utf-8

from __future__ import annotations
import configparser
import logging
from typing import Iterable


from nxtomomill.io.config.optionlevel import OptionLevel, filter_options_level_items
from nxtomo.nxobject.nxdetector import FieldOfView
from nxtomomill.utils import FileExtension
from nxtomo.nxobject.nxsource import ProbeType, SourceType

__all__ = ["ConfigBase", "ConfigSourceSection"]


[docs]class ConfigSourceSection: # SOURCE SECTION SOURCE_SECTION_DK = "SOURCE_SECTION" INSTRUMENT_NAME_DK = "instrument_name" SOURCE_NAME_DK = "source_name" SOURCE_TYPE_DK = "source_type" SOURCE_PROBE_DK = "source_probe" COMMENTS_SOURCE_SECTION_DK = { SOURCE_SECTION_DK: "section dedicated to source definition.\n", INSTRUMENT_NAME_DK: "name of the instrument", SOURCE_NAME_DK: "name of the source", SOURCE_TYPE_DK: f"type of the source. Must be one of {SourceType.values()}", SOURCE_PROBE_DK: f"source probe. Must be one of {ProbeType.values()}", } LEVEL_SOURCE_SECTION = { INSTRUMENT_NAME_DK: OptionLevel.ADVANCED, SOURCE_NAME_DK: OptionLevel.ADVANCED, SOURCE_TYPE_DK: OptionLevel.ADVANCED, SOURCE_PROBE_DK: OptionLevel.ADVANCED, } def load_source_section(self, dict_: dict) -> None: if self.INSTRUMENT_NAME_DK in dict_: self.instrument_name = dict_.get(self.INSTRUMENT_NAME_DK) if self.SOURCE_NAME_DK in dict_: self.source_name = dict_.get(self.SOURCE_NAME_DK) if self.SOURCE_TYPE_DK in dict_: self.source_type = dict_[self.SOURCE_TYPE_DK] if self.SOURCE_PROBE_DK in dict_: self.source_probe = dict_[self.SOURCE_PROBE_DK] def _source_section_to_dict(self, level) -> dict: res = { self.INSTRUMENT_NAME_DK: self.instrument_name or "", self.SOURCE_NAME_DK: self.source_name or "", self.SOURCE_TYPE_DK: ( self.source_type.value if self.source_type is not None else "" ), self.SOURCE_PROBE_DK: ( self.source_probe.value if self.source_probe is not None else "" ), } return filter_options_level_items( dict_=res, level=level, level_ref=self.LEVEL_SOURCE_SECTION )
[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) -> str | None: return self._output_file @output_file.setter def output_file(self, output_file: str | None): 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) -> FieldOfView | None: return self._field_of_view @field_of_view.setter def field_of_view(self, fov: FieldOfView | str | None): 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 translation_z_keys(self) -> Iterable: return self._translation_z_keys @translation_z_keys.setter def translation_z_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._translation_z_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())