Source code for atomsmltr.utils.infostring

"""infostrings
==================

Here we implement the ``InfoString`` class, that allows to gather
different parameters from objects and to display them in a tree format.

This is used in the ``print_info()`` method that is present in most
classes accross the module.

Examples
--------------------

Run a simulation with one initial condition vector

.. code-block:: python

        from atomsmltr.utils.infostring import InfoString

        info = InfoString("PARAMETERS")
        info.add_section("First Section")
        info.add_element("elem", "param")
        info.add_element("x", f"{25.4e-6:.2e}µm")
        info.add_element("y", f"{15.8:.2f} mW")
        info.add_section("Another section")
        info.add_element("elem", "param")
        info.add_element("elem2", "param2")

        print(info.generate())

This prints :

.. code-block::

        ──────────────
        | PARAMETERS |
        ──────────────
        . First Section :
        ├── elem : param
        ├── x : 2.54e-05µm
        └── y : 15.80 mW

        . Another section :
        ├── elem : param
        └── elem2 : param2

"""

# % IMPORTS

from collections import OrderedDict

# % CONSTANTS

HEADER = ". {} :\n"
PARAM = "  ├── {} : {}\n"
PARAMSINGLE = "  ├── {}\n"
LPARAM = "  └── {} : {}\n\n"
LPARAMSINGLE = "  └── {}\n\n"
TITLE = "| {} |\n"

# % CLASS


[docs] class InfoString(object): """Generates info strings Parameters ---------- title : str the title of the infostring object """ def __init__(self, title: str): self.__title = title self.__elements = OrderedDict() self.__current_section = "" @property def elements(self): """OrderedDict: the elements inside the infostring object""" return self.__elements @property def title(self) -> str: """str: titile of the infostring object""" return self.title @title.setter def title(self, value: str): assert isinstance(value, str), "title should be a string" self.__title = value
[docs] def add_section(self, name: str): """adds a new section for parameters Parameters ---------- name : str the section name """ if name in self.__elements: raise Warning(f"section '{name}' already exists") self.__elements[name] = OrderedDict() self.__current_section = name
[docs] def add_element(self, name: str, value: str = None, section: str = None): """adds an element in a given section Parameters ---------- name : str name of the element value : str, optional value of the element, by default None section : str, optional name of the section. If None is given, the element is added to the lastly used section, by default None """ if section is None: section = self.__current_section if section not in self.__elements: raise Warning(f"section '{section}' does not exist") # switch current section self.__current_section = section if name in self.__elements[section]: raise Warning(f"section '{section}' already has an element {name}") self.__elements[section][name] = value
[docs] def rm_element(self, name: str, section: str = None): """removes an element from a section Parameters ---------- name : str name of the element to remove section : str, optional name of the section. If None is given, the element is removed from the lastly used section, by default None """ if section is None: section = self.__current_section if section not in self.__elements: raise Warning(f"section '{section}' does not exist") # switch current section self.__current_section = section if name not in self.__elements[section]: raise Warning(f"section '{section}' does not have an element {name}") del self.__elements[section][name]
[docs] def absorb_section(self, info, target_section: str, new_name: str = None): """incorporates a section from another infostring object Parameters ---------- info : InfoString the infostring object from which we take the section target_section : str name of the section to incorporate new_name : str, optional name of the incorporated section. If None is given we use the name of the original section, by default None """ # assert assert isinstance(info, InfoString), "'info' should be an InfoString object" # get info info_dic = info.elements assert ( target_section in info_dic ), f"This info object does not have a '{target_section}' section" if new_name is None: new_name = target_section # absorb self.add_section(new_name) for name, value in info_dic[target_section].items(): self.add_element(name, value)
[docs] def merge(self, info, prefix=""): """merges an entire infostring Parameters ---------- info : InfoString infostring object to merge prefix : str, optional prefix added to the names of the sections incorporated from the merged infostring, by default "" """ # assert assert isinstance(info, InfoString), "'info' should be an InfoString object" # merge for section, elements in info.elements.items(): self.add_section(prefix + section) for name, value in elements.items(): self.add_element(name, value)
[docs] def generate(self, display_title=True): """generates a string from the infostring object Parameters ---------- display_title : bool, optional whether to display the title, by default True Returns ------- out_str: str a string with all parameters from the info string """ # init out = [] # title if display_title: title = TITLE.format(self.__title) line = "─" * (len(title) - 1) + "\n" out.append(line + title + line) # params for section, elements in self.__elements.items(): out.append(HEADER.format(section)) for name, value in elements.items(): if value is None: out.append(PARAMSINGLE.format(name)) else: out.append(PARAM.format(name, value)) # remove last an replace by a last param string out.pop() if value is None: out.append(LPARAMSINGLE.format(name)) else: out.append(LPARAM.format(name, value)) out_str = "".join(out) return out_str