# -*- coding: utf-8 -*-
"""Contain utils required by cenv-tool."""
import os
from pathlib import Path
from subprocess import CalledProcessError
from subprocess import check_output
from subprocess import STDOUT
from typing import List
from typing import NoReturn
import jinja2
import six
import yaml
from marshmallow import ValidationError
from cenv_tool.schemata import SMetaYaml
CYAN = '\033[1;36m'
GREEN = '\033[1;32m'
RED = '\033[1;91m'
NCOLOR = '\033[0m'
BOLD = '\033[1;37m'
[docs]class CenvProcessError(Exception):
"""Represent a process error during cenv execution."""
[docs]def message(
*, text: str, color: str, special: str = None, indent: int = 1
) -> NoReturn:
"""Print passed ``text`` in the passed ``color`` on terminal.
Parameters:
text: the text to print colored on terminal.
color: the color of the text to print.
special: special kind of message to print. Available are ``'row'`` and
``'end'``.
indent: the indent to use for the text.
"""
color_mapping = {
'red': RED,
'green': GREEN,
'cyan': CYAN,
'bold': BOLD,
}
if indent == 1:
indent_prefix = ' ' * indent
else:
indent_prefix = ' ' + '│ ' * (indent - 1)
special_mapping = {
'row': f'{indent_prefix}├── ',
'end': f'{indent_prefix}└── ',
}
if special:
prefix = special_mapping[special]
else:
prefix = ''
print(f'{prefix}{color_mapping[color]}{text}{NCOLOR}')
[docs]def run_in_bash(cmd: str) -> str:
"""Run passed ``cmd`` inside bash using :func:`subprocess.check_output`.
Parameters:
cmd: the command to execute.
Returns:
the output of the ran command.
"""
try:
result = check_output([cmd], shell=True, stderr=STDOUT)
except CalledProcessError as err:
error_message = err.output.decode('utf8').split('\n')
message(text='the following error occured:', color='red')
for line in error_message:
message(text=line, color='bold')
raise CenvProcessError(str(err.output))
return result.strip().decode('ascii')
[docs]class _NullUndefined(jinja2.Undefined):
"""Handle jinja2-variables with undefined content of ``meta.yaml.``"""
[docs] def __unicode__(self):
"""Replace unicode dunder of this class."""
return six.text_type(self._undefined_name)
[docs] def __getattr__(self, attribute_name: str):
"""Replace getattr dunder of this class."""
return six.text_type(f'{self}.{attribute_name}')
[docs] def __getitem__(self, attribute_name: str):
"""Replace getitem dunder of this class."""
return f'{self}["{attribute_name}"]'
[docs]class _StrDict(dict):
"""Handle dictionaries for jinja2-variables of ``meta.yaml``."""
[docs] def __getitem__(self, key: str, default: str = '') -> str:
"""Replace getitem dunder of this class."""
return self[key] if key in self else default
[docs]def read_config():
"""Read the config file for cenv from the users-home path if it exists.
If there is no user-config-file the default one is used.
Returns:
the content of the read config file.
"""
user_config_path = Path.home() / '.config/cenv/cenv.yml'
default_config_path = Path(__file__).parent / 'cenv.yml'
# Collect settings from config file .cenv.yml
main_config = yaml.safe_load(default_config_path.open().read())
# if a user-config-file exists, read the content and update the main-config
if user_config_path.exists():
user_config = yaml.safe_load(user_config_path.open().read())
main_config.update(user_config)
return main_config