Loading .gitignore +3 −0 Original line number Diff line number Diff line Loading @@ -5,3 +5,6 @@ */raw/ .idea/ *.cfg *.log No newline at end of file color.py +12 −12 Original line number Diff line number Diff line Loading @@ -21,26 +21,26 @@ import re from ammsml.config import constants as C IS_COLOR: bool = True if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): IS_COLOR = False AMMSML_COLOR: bool = True if C.AMMSML_NOCOLOR: AMMSML_COLOR = False elif not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): AMMSML_COLOR = False else: try: import curses curses.setupterm() if curses.tigetnum('colors') < 0: IS_COLOR = False AMMSML_COLOR = False except ImportError: # curses library was not found pass except curses.error: # curses returns an error (e.g. could not find terminal) IS_COLOR = False AMMSML_COLOR = False # TODO implement some Config entry for Color # if C.IS_FORCE_COLOR: # IS_COLOR = True if C.AMMSML_FORCE_COLOR: AMMSML_COLOR = True # --- begin "pretty" # Loading Loading @@ -88,7 +88,7 @@ def parsecolor(color): def stringc(text: str, color) -> str: """String in color.""" if IS_COLOR: if AMMSML_COLOR: color_code = parsecolor(color) return "\n".join(["\033[%sm%s\033[0m" % (color_code, t) for t in text.split('\n')]) else: Loading @@ -98,13 +98,13 @@ def stringc(text: str, color) -> str: def colorize(lead, num, color): """ Print 'lead' = 'num' in 'color' """ s = "%s=%-4s" % (lead, str(num)) if num != 0 and IS_COLOR and color is not None: if num != 0 and AMMSML_COLOR and color is not None: s = stringc(s, color) return s def hostcolor(host: str, stats: list, color: bool = True) -> str: if IS_COLOR and color: if AMMSML_COLOR and color: if stats['failures'] != 0 or stats['unreachable'] != 0: return "%-37s" % stringc(host, C.COLOR_ERROR) elif stats['changed'] != 0: Loading display.py +32 −11 Original line number Diff line number Diff line Loading @@ -25,13 +25,14 @@ import time import textwrap import subprocess import logging import getpass from struct import unpack, pack from termios import TIOCGWINSZ from ammsml.utils.color import stringc from ammsml.utils.parsing import to_text, boolean from ammsml.utils.errors import AMMSMLError from ammsml.utils.errors import AMMSMLError, AMMSMLAssertionError from ammsml.utils.singleton import Singleton, with_metaclass from ammsml.config import constants as C from ammsml.utils.wrap import wrap_var Loading @@ -47,15 +48,16 @@ class FilterBlackList(logging.Filter): logger = None # TODO: make this a callback event instead # if getattr(C, 'DEFAULT_LOG_PATH'): # path = C.DEFAULT_LOG_PATH # if path and (os.path.exists(path) and os.access(path, os.W_OK)) or os.access(os.path.dirname(path), os.W_OK): # logging.basicConfig(filename=path, level=logging.INFO, format='%(asctime)s p=%(user)s u=%(process)d | %(message)s') # logger = logging.LoggerAdapter(logging.getLogger('ammsml'), {'user': getpass.getuser()}) # for handler in logging.root.handlers: # handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', []))) # else: # print("[WARNING]: log file at %s is not writeable and we cannot create it, aborting\n" % path, file=sys.stderr) if getattr(C, 'DEFAULT_LOG_PATH'): path = C.DEFAULT_LOG_PATH if path and (os.path.exists(path) and os.access(path, os.W_OK)) or os.access(os.path.dirname(path), os.W_OK): logging.basicConfig(filename=path, level=logging.INFO, format='%(asctime)s p=%(user)s u=%(process)d | %(message)s') logger = logging.LoggerAdapter(logging.getLogger('ammsml'), {'user': getpass.getuser()}) for handler in logging.root.handlers: handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', []))) else: print("[WARNING]: log file at %s is not writeable and we cannot create it, aborting\n" % path, file=sys.stderr) # map color to log levels Loading Loading @@ -150,6 +152,26 @@ class Display(with_metaclass(Singleton, object)): if e.errno != errno.EPIPE: raise if logger and not screen_only: # We first convert to a byte string so that we get rid of # color and characters that are invalid in the user's locale msg2 = to_text(nocolor.lstrip(u'\n')) if sys.version_info >= (3,): # Convert back to text string on python3 msg2 = to_text(msg2, self._output_encoding(stderr=stderr)) lvl = logging.INFO if color: # set logger level based on color (not great) try: lvl = color_to_log_level[color] except KeyError: # this should not happen, but JIC raise AMMSMLAssertionError('Invalid color supplied to display: %s' % color) # actually log logger.log(lvl, msg2) def v(self, msg, host=None): return self.verbose(msg, host=host, caplevel=0) Loading @@ -169,7 +191,6 @@ class Display(with_metaclass(Singleton, object)): return self.verbose(msg, host=host, caplevel=5) def debug(self, msg, host=None): # TODO boolean() neccesary? if boolean(C.DEFAULT_DEBUG): if host is None: self.display("%6d %0.5f: %s" % (os.getpid(), time.time(), msg), color=C.COLOR_DEBUG) Loading errors/__init__.py +5 −0 Original line number Diff line number Diff line Loading @@ -195,3 +195,8 @@ class TemplateParsingError(AMMSMLError): class AMMSMLOptionsError(AMMSMLError): pass class AMMSMLAssertionError(AMMSMLError, AssertionError): """Invalid assertion""" pass path.py +2 −2 Original line number Diff line number Diff line Loading @@ -38,9 +38,9 @@ def unfrackpath(path, follow=True, basedir=None): """ Returns a path that is free of symlinks (if follow=True), environment variables, relative path traversals and symbols (~) :param path: A byte or text string representing a path to be canonicalized :param path: A byte or text string representing a path to be canonised :param follow: A boolean to indicate of symlinks should be resolved or not :raises UnicodeDecodeError: If the canonicalized version of the path :raises UnicodeDecodeError: If the canonised version of the path contains non-utf8 byte sequences. :rtype: A text string :returns: An absolute path with symlinks, environment variables, and tilde Loading Loading
.gitignore +3 −0 Original line number Diff line number Diff line Loading @@ -5,3 +5,6 @@ */raw/ .idea/ *.cfg *.log No newline at end of file
color.py +12 −12 Original line number Diff line number Diff line Loading @@ -21,26 +21,26 @@ import re from ammsml.config import constants as C IS_COLOR: bool = True if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): IS_COLOR = False AMMSML_COLOR: bool = True if C.AMMSML_NOCOLOR: AMMSML_COLOR = False elif not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty(): AMMSML_COLOR = False else: try: import curses curses.setupterm() if curses.tigetnum('colors') < 0: IS_COLOR = False AMMSML_COLOR = False except ImportError: # curses library was not found pass except curses.error: # curses returns an error (e.g. could not find terminal) IS_COLOR = False AMMSML_COLOR = False # TODO implement some Config entry for Color # if C.IS_FORCE_COLOR: # IS_COLOR = True if C.AMMSML_FORCE_COLOR: AMMSML_COLOR = True # --- begin "pretty" # Loading Loading @@ -88,7 +88,7 @@ def parsecolor(color): def stringc(text: str, color) -> str: """String in color.""" if IS_COLOR: if AMMSML_COLOR: color_code = parsecolor(color) return "\n".join(["\033[%sm%s\033[0m" % (color_code, t) for t in text.split('\n')]) else: Loading @@ -98,13 +98,13 @@ def stringc(text: str, color) -> str: def colorize(lead, num, color): """ Print 'lead' = 'num' in 'color' """ s = "%s=%-4s" % (lead, str(num)) if num != 0 and IS_COLOR and color is not None: if num != 0 and AMMSML_COLOR and color is not None: s = stringc(s, color) return s def hostcolor(host: str, stats: list, color: bool = True) -> str: if IS_COLOR and color: if AMMSML_COLOR and color: if stats['failures'] != 0 or stats['unreachable'] != 0: return "%-37s" % stringc(host, C.COLOR_ERROR) elif stats['changed'] != 0: Loading
display.py +32 −11 Original line number Diff line number Diff line Loading @@ -25,13 +25,14 @@ import time import textwrap import subprocess import logging import getpass from struct import unpack, pack from termios import TIOCGWINSZ from ammsml.utils.color import stringc from ammsml.utils.parsing import to_text, boolean from ammsml.utils.errors import AMMSMLError from ammsml.utils.errors import AMMSMLError, AMMSMLAssertionError from ammsml.utils.singleton import Singleton, with_metaclass from ammsml.config import constants as C from ammsml.utils.wrap import wrap_var Loading @@ -47,15 +48,16 @@ class FilterBlackList(logging.Filter): logger = None # TODO: make this a callback event instead # if getattr(C, 'DEFAULT_LOG_PATH'): # path = C.DEFAULT_LOG_PATH # if path and (os.path.exists(path) and os.access(path, os.W_OK)) or os.access(os.path.dirname(path), os.W_OK): # logging.basicConfig(filename=path, level=logging.INFO, format='%(asctime)s p=%(user)s u=%(process)d | %(message)s') # logger = logging.LoggerAdapter(logging.getLogger('ammsml'), {'user': getpass.getuser()}) # for handler in logging.root.handlers: # handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', []))) # else: # print("[WARNING]: log file at %s is not writeable and we cannot create it, aborting\n" % path, file=sys.stderr) if getattr(C, 'DEFAULT_LOG_PATH'): path = C.DEFAULT_LOG_PATH if path and (os.path.exists(path) and os.access(path, os.W_OK)) or os.access(os.path.dirname(path), os.W_OK): logging.basicConfig(filename=path, level=logging.INFO, format='%(asctime)s p=%(user)s u=%(process)d | %(message)s') logger = logging.LoggerAdapter(logging.getLogger('ammsml'), {'user': getpass.getuser()}) for handler in logging.root.handlers: handler.addFilter(FilterBlackList(getattr(C, 'DEFAULT_LOG_FILTER', []))) else: print("[WARNING]: log file at %s is not writeable and we cannot create it, aborting\n" % path, file=sys.stderr) # map color to log levels Loading Loading @@ -150,6 +152,26 @@ class Display(with_metaclass(Singleton, object)): if e.errno != errno.EPIPE: raise if logger and not screen_only: # We first convert to a byte string so that we get rid of # color and characters that are invalid in the user's locale msg2 = to_text(nocolor.lstrip(u'\n')) if sys.version_info >= (3,): # Convert back to text string on python3 msg2 = to_text(msg2, self._output_encoding(stderr=stderr)) lvl = logging.INFO if color: # set logger level based on color (not great) try: lvl = color_to_log_level[color] except KeyError: # this should not happen, but JIC raise AMMSMLAssertionError('Invalid color supplied to display: %s' % color) # actually log logger.log(lvl, msg2) def v(self, msg, host=None): return self.verbose(msg, host=host, caplevel=0) Loading @@ -169,7 +191,6 @@ class Display(with_metaclass(Singleton, object)): return self.verbose(msg, host=host, caplevel=5) def debug(self, msg, host=None): # TODO boolean() neccesary? if boolean(C.DEFAULT_DEBUG): if host is None: self.display("%6d %0.5f: %s" % (os.getpid(), time.time(), msg), color=C.COLOR_DEBUG) Loading
errors/__init__.py +5 −0 Original line number Diff line number Diff line Loading @@ -195,3 +195,8 @@ class TemplateParsingError(AMMSMLError): class AMMSMLOptionsError(AMMSMLError): pass class AMMSMLAssertionError(AMMSMLError, AssertionError): """Invalid assertion""" pass
path.py +2 −2 Original line number Diff line number Diff line Loading @@ -38,9 +38,9 @@ def unfrackpath(path, follow=True, basedir=None): """ Returns a path that is free of symlinks (if follow=True), environment variables, relative path traversals and symbols (~) :param path: A byte or text string representing a path to be canonicalized :param path: A byte or text string representing a path to be canonised :param follow: A boolean to indicate of symlinks should be resolved or not :raises UnicodeDecodeError: If the canonicalized version of the path :raises UnicodeDecodeError: If the canonised version of the path contains non-utf8 byte sequences. :rtype: A text string :returns: An absolute path with symlinks, environment variables, and tilde Loading