From 51cd87243c52977f5afcb928bbbb8fcfa409b5b4 Mon Sep 17 00:00:00 2001 From: Christof Schulze Date: Wed, 9 Oct 2019 21:40:49 +0200 Subject: [PATCH] added some new display options, wrapping unsafe parameters, Copyright and License information added --- __init__.py | 18 ++++++- color.py | 18 ++++++- display.py | 134 +++++++++++++++++++++++++++++++++++++++++++++-- errors.py | 19 ++++++- parseing_yaml.py | 18 +++++++ parsing.py | 18 ++++++- path.py | 20 ++++++- singleton.py | 22 ++++++-- six.py | 20 ++++++- wrap.py | 69 ++++++++++++++++++++++++ 10 files changed, 340 insertions(+), 16 deletions(-) create mode 100644 wrap.py diff --git a/__init__.py b/__init__.py index 5f7ce86..5fc5475 100644 --- a/__init__.py +++ b/__init__.py @@ -1 +1,17 @@ -#!/usr/bin/env python3 \ No newline at end of file +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . diff --git a/color.py b/color.py index 3dcf146..6743511 100644 --- a/color.py +++ b/color.py @@ -1,4 +1,20 @@ -#!/usr/bin/env python3 +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . import sys import re diff --git a/display.py b/display.py index 29cedb3..ad5bfae 100644 --- a/display.py +++ b/display.py @@ -1,18 +1,40 @@ -#!/usr/bin/env python3 +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . + import os import sys +import locale import errno +import fcntl import time import textwrap +import subprocess + +from struct import unpack, pack +from termios import TIOCGWINSZ from utils.color import stringc -from utils.color import BColors from utils.parsing import to_text from utils.errors import AMMSMLError from utils.singleton import Singleton from config import constants as C from utils.six import with_metaclass +from utils.wrap import wrap_var b_COW_PATHS = ( @@ -22,6 +44,7 @@ b_COW_PATHS = ( b"/opt/local/bin/cowsay", # MacPorts path for cowsay ) + class Display(with_metaclass(Singleton, object)): def __init__(self, verbosity: int=0): @@ -34,6 +57,35 @@ class Display(with_metaclass(Singleton, object)): self._warns = {} self._errors = {} + self.b_cowsay = None + self.noncow = C.AMMSML_COW_SELECTION + + self.set_cowsay_info() + + if self.b_cowsay: + try: + cmd = subprocess.Popen([self.b_cowsay, "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = cmd.communicate() + self.cows_available = set([to_text(c) for c in out.split()]) + if C.AMMSML_COW_WHITELIST and any(C.AMMSML_COW_WHITELIST): + self.cows_available = set(C.AMNMSML_COW_WHITELIST).intersection(self.cows_available) + except Exception: + # could not execute cowsay for some reason + self.b_cowsay = False + + self._set_column_width() + + def set_cowsay_info(self): + if C.AMMSML_NOCOWS: + return + + if C.AMMSML_COW_PATH: + self.b_cowsay = C.AMMSML_COW_PATH + else: + for b_cow_path in b_COW_PATHS: + if os.path.exists(b_cow_path): + self.b_cowsay = b_cow_path + def display(self, msg, color=None, stderr=False, screen_only=False, log_only=False): """ Display a message to the user Note: msg *must* be a unicode string to prevent UnicodeError tracebacks. @@ -157,4 +209,80 @@ class Display(with_metaclass(Singleton, object)): if star_len <= 3: star_len = 3 stars = u"*" * star_len - self.display(u"\n%s %s" % (msg, stars), color=color) \ No newline at end of file + self.display(u"\n%s %s" % (msg, stars), color=color) + + def error(self, msg, wrap_text=True): + if wrap_text: + new_msg = "\n[ERROR]: %s" % msg + wrapped = textwrap.wrap(new_msg, self.columns) + new_msg = "\n".join(wrapped) + "\n" + else: + new_msg = "ERROR! %s" % msg + if new_msg not in self._errors: + self.display(new_msg, color=C.COLOR_ERROR, stderr=True) + self._errors[new_msg] = 1 + + @staticmethod + def prompt(msg): + prompt_string = to_text(msg, encoding=Display._output_encoding()) + if sys.version_info >= (3,): + # Convert back into text on python3. We do this double conversion + # to get rid of characters that are illegal in the user's locale + prompt_string = to_text(prompt_string) + + return input(prompt_string) + + def do_var_prompt(self, varname, private=True, prompt=None, confirm=False, default=None, unsafe=None): + + result = None + if sys.__stdin__.isatty(): + + do_prompt = self.prompt + + if prompt and default is not None: + msg = "%s [%s]: " % (prompt, default) + elif prompt: + msg = "%s: " % prompt + else: + msg = 'input for %s: ' % varname + + if confirm: + while True: + result = do_prompt(msg, private) + second = do_prompt("confirm " + msg, private) + if result == second: + break + self.display("***** VALUES ENTERED DO NOT MATCH ****") + else: + result = do_prompt(msg, private) + else: + result = None + self.warning("Not prompting as we are not in interactive mode") + + # if result is false and default is not None + if not result and default is not None: + result = default + + # handle utf-8 chars + result = to_text(result, errors='surrogate_or_strict') + + if unsafe: + result = wrap_var(result) + return result + + @staticmethod + def _output_encoding(stderr=False): + encoding = locale.getpreferredencoding() + # https://bugs.python.org/issue6202 + # Python2 hardcodes an obsolete value on Mac. Use MacOSX defaults + # instead. + if encoding in ('mac-roman',): + encoding = 'utf-8' + return encoding + + def _set_column_width(self): + if os.isatty(0): + tty_size = unpack('HHHH', fcntl.ioctl(0, TIOCGWINSZ, pack('HHHH', 0, 0, 0, 0)))[1] + else: + tty_size = 0 + self.columns = max(79, tty_size - 1) \ No newline at end of file diff --git a/errors.py b/errors.py index f071333..916d54d 100644 --- a/errors.py +++ b/errors.py @@ -1,4 +1,20 @@ -#!/usr/bin/env python3 +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . class AMMSMLError(Exception): @@ -19,4 +35,3 @@ class TemplateParsingError(AMMSMLError): class AMMSMLOptionsError(AMMSMLError): pass - diff --git a/parseing_yaml.py b/parseing_yaml.py index 51304e6..0fa3d0a 100644 --- a/parseing_yaml.py +++ b/parseing_yaml.py @@ -1,3 +1,21 @@ +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . + import yaml from utils.parsing import to_text diff --git a/parsing.py b/parsing.py index f36d7df..79abeea 100644 --- a/parsing.py +++ b/parsing.py @@ -1,4 +1,20 @@ -#!/usr/bin/env python3 +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . def to_text(obj, encoding='utf-8'): diff --git a/path.py b/path.py index 91d6a08..fdd5843 100644 --- a/path.py +++ b/path.py @@ -1,4 +1,20 @@ -#!/usr/bin/env python3 +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . import os import shutil @@ -113,7 +129,7 @@ def cleanup_tmp_file(path, warn=False): except Exception as e: if warn: # Importing here to avoid circular import - from ansible.utils.display import Display + from utils.display import Display display = Display() display.display(u'Unable to remove temporary file {0}'.format(to_text(e))) except Exception: diff --git a/singleton.py b/singleton.py index 429ac78..36bdc6d 100644 --- a/singleton.py +++ b/singleton.py @@ -1,6 +1,20 @@ -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -__metaclass__ = type +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . from threading import RLock @@ -23,4 +37,4 @@ class Singleton(type): if cls.__instance is None: cls.__instance = super(Singleton, cls).__call__(*args, **kw) - return cls.__instance \ No newline at end of file + return cls.__instance diff --git a/six.py b/six.py index 3cbad48..916f939 100644 --- a/six.py +++ b/six.py @@ -1,4 +1,20 @@ - +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . def with_metaclass(meta, *bases): @@ -14,4 +30,4 @@ def with_metaclass(meta, *bases): @classmethod def __prepare__(cls, name, this_bases): return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) \ No newline at end of file + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/wrap.py b/wrap.py new file mode 100644 index 0000000..807ce92 --- /dev/null +++ b/wrap.py @@ -0,0 +1,69 @@ +# GNU Lesser General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/lgpl-3.0.txt) +# (c) 2019, Christof Schulze +# +# This file is part of Ammsml +# +# Ammsml is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ammsml is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Ammsml. If not, see . + +from collections.abc import Mapping, MutableSequence, Set + + +__all__ = ['AMMSMLUnsafe', 'wrap_var'] + + +class AMMSMLUnsafe(object): + __UNSAFE__ = True + + +class AMMSMLUnsafeBytes(bytes, AMMSMLUnsafe): + pass + + +class AMMSMLUnsafeText(str, AMMSMLUnsafe): + pass + + +def _wrap_dict(v): + for k in v.keys(): + if v[k] is not None: + v[wrap_var(k)] = wrap_var(v[k]) + return v + + +def _wrap_list(v): + for idx, item in enumerate(v): + if item is not None: + v[idx] = wrap_var(item) + return v + + +def _wrap_set(v): + return set(item if item is None else wrap_var(item) for item in v) + + +def wrap_var(v): + if isinstance(v, AMMSMLUnsafe): + return v + if isinstance(v, Mapping): + v = _wrap_dict(v) + elif isinstance(v, MutableSequence): + v = _wrap_list(v) + elif isinstance(v, Set): + v = _wrap_set(v) + elif isinstance(v, bytes): + v = AMMSMLUnsafeBytes(v) + elif isinstance(v, str): + v = AMMSMLUnsafeText(v) + + return v -- GitLab