Source code for pyprec.utils.utils

"""This module implements general utility functions."""
from importlib.resources import files
from pathlib import Path, PosixPath
from string import Template, ascii_uppercase
import yaml


[docs]def get_template_path(template_name: str) -> Path: """Returns the query template as a Path. This implementation retrieves the query path independently of the directory where the Python interpreter has been run from. Parameters ---------- template_name: str The name of the template to be found in the source file. Returns ------- Path The path to the query template. """ return files("pyprec.templates").joinpath(template_name)
# ------------------------------------------------------------------------------ # runcard utilities
[docs]def path_constructor(loader, node): """PyYaml utility function.""" value = loader.construct_scalar(node) return Path(value)
[docs]def path_representer(dumper, data): """PyYaml utility function.""" return dumper.represent_scalar("!Path", "%s" % data)
[docs]def load_runcard(runcard_file: Path) -> dict: """Load runcard from yaml file. Parameters ---------- runcard_file: Path The yaml to load the dictionary from. Returns ------- runcard: dict The loaded settings dictionary. Note ---- The pathlib.Path objects are automatically loaded if they are encoded with the following syntax: .. code-block:: yaml path: !Path 'path/to/file' """ if not isinstance(runcard_file, Path): runcard_file = Path(runcard_file) yaml.add_constructor("!Path", path_constructor) with open(runcard_file, "r") as stream: runcard = yaml.load(stream, Loader=yaml.FullLoader) return runcard
[docs]def save_runcard(fname: Path, setup: dict): """Save runcard to yaml file. Parameters ---------- fname: Path The yaml output file. setup: Path The settings dictionary to be dumped. Note ---- pathlib.PosixPath objects are automatically loaded. """ yaml.add_representer(PosixPath, path_representer) with open(fname, "w") as f: yaml.dump(setup, f, indent=4)
# ------------------------------------------------------------------------------ # Template utilities
[docs]def get_mapping_from_args(arg: dict = {}, /, **kwargs) -> dict: """Unifies the parameters in a unique dictionary map. If there are duplicates between ``arg`` dictionary keys and any parameter in ``**kwargs``, keyword arguments have always the returned dictionary. Parameters ---------- arg: dict The input dictionary. **kwargs Optional keyword arguments. Returns ------- mapping: dict The unique output mapping. """ return arg | kwargs
[docs]class CaseSensitiveTemplate(Template): """Class re-defining Template for case sensitive string substitution. The ``substitute`` and ``safe_substitute`` methods treat differently the placeholders with different capitalization in the template file. Example -------- >>> template = ''' ... ${prefix} is lower-case ... ${Prefix} is capitalized ... ${PREFIX} is uppercase ... ''' >>> t = CaseSensitiveTemplate(template) >>> replace_dict = {"prefix": "foo"} >>> subs = t.substitute(replace_dict) >>> print(subs) foo is lower-case Foo is capitalized FOO is uppercase """ def __init__(self, *args, **kws): super().__init__(*args, **kws)
[docs] def do_template_based_capitalization(self, mapping: dict): """Adds capitalized or full uppercase keys to replace dictionary. Parameters ---------- mapping: dict The key-value pairs to be inserted in the template fields. """ matches = self.pattern.findall(self.template) for match in matches: key = next(filter(None, match)) if key and key[0] in ascii_uppercase: if key == key.upper(): # full uppercase mapping[key.upper()] = mapping[key.lower()].upper() else: # first letter capitalization mapping[key.capitalize()] = mapping[key.lower()].capitalize()
[docs] def safe_substitute(self, mapping={}, /, **kwds) -> str: """Safe substitution methods with case sensitive capitalization. Refer to `docs <https://docs.python.org/3/library/string.html#string.Template>`_ for function documentation. """ new_mapping = get_mapping_from_args(mapping, **kwds) self.do_template_based_capitalization(new_mapping) return super().safe_substitute(new_mapping)
[docs] def substitute(self, mapping={}, /, **kwds) -> str: """Safe substitution methods with case sensitive capitalization. Refer to `docs <https://docs.python.org/3/library/string.html#string.Template>`_ for function documentation. """ new_mapping = get_mapping_from_args(mapping, **kwds) self.do_template_based_capitalization(new_mapping) return super().substitute(new_mapping)
# ------------------------------------------------------------------------------ # console utilities
[docs]def get_color_to_sgr_codes() -> dict: """Return Select Graphic Rendiction (SGR) sequence in dictionary form. Note ---- Check out this `link <https://chrisyeh96.github.io/2020/03/28/terminal-colors.html>`_ for more information about terminal colors and SGR codes. Returns ------- codes: dict The terminal colorcodes """ codes = {"default": "\x1b[0m"} _colors = [ ("black", "gray"), ("red", "light red"), ("green", "light green"), ("yellow", "light yellow"), ("blue", "light blue"), ("magenta", "light magenta"), ("cyan", "light cyan"), ("white", "bright white"), ] for i, (dark, light) in enumerate(_colors, 30): codes[dark] = "\x1b[%im" % i codes[light] = "\x1b[%im" % (i + 60) return codes
[docs]def colorize(text: str, color: str) -> str: """Changes the printed color of the input string. If the color name does not exists, returns default color. Parameters ---------- text: str The text to be colored. color: str The color name. """ codes = get_color_to_sgr_codes() code = codes.get(color, None) if code is None: return text return "".join([codes[color], text, codes["default"]])
[docs]def boldface(text: str) -> str: """Returns input boldface version. Parameters ---------- text: str The test to be boldfaced. Return ------ str The boldface version of the input. """ return "".join(["\x1b[1m", text, "\x1b[0m"])