From: Frank Brehm Date: Mon, 13 Mar 2017 12:04:36 +0000 (+0100) Subject: Adding some python modules X-Git-Tag: 0.1.2~275 X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=256f03527a69a9842a446b1b98179721e0c4005d;p=pixelpark%2Fadmin-tools.git Adding some python modules --- diff --git a/pp_lib/__init__.py b/pp_lib/__init__.py new file mode 100644 index 0000000..3934b42 --- /dev/null +++ b/pp_lib/__init__.py @@ -0,0 +1,6 @@ +#!/bin/env python3 +# -*- coding: utf-8 -*- + +__version__ = '0.1.1' + +# vim: ts=4 et list diff --git a/pp_lib/colored.py b/pp_lib/colored.py new file mode 100644 index 0000000..7e223a7 --- /dev/null +++ b/pp_lib/colored.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@summary: additional logging formatter for colored output via console +""" + +# Standard modules +import logging +# import os.path +# import sys +import copy + +# Third party modules + +# Own modules + +# import pb_provisioning.common + +# from pb_provisioning.common import to_unicode_or_bust, to_utf8_or_bust + +__version__ = '0.1.3' + +# ============================================================================= +# Color coding module variables and helper functions + +COLOR_CODE = { + 'ENDC': 0, # RESET COLOR + 'BOLD': 1, + 'UNDERLINE': 4, + 'BLINK': 5, + 'INVERT': 7, + 'CONCEALD': 8, + 'STRIKE': 9, + 'GREY30': 90, + 'GREY40': 2, + 'GREY65': 37, + 'GREY70': 97, + 'GREY20_BG': 40, + 'GREY33_BG': 100, + 'GREY80_BG': 47, + 'GREY93_BG': 107, + 'DARK_RED': 31, + 'RED': 91, + 'RED_BG': 41, + 'LIGHT_RED_BG': 101, + 'DARK_YELLOW': 33, + 'YELLOW': 93, + 'YELLOW_BG': 43, + 'LIGHT_YELLOW_BG': 103, + 'DARK_BLUE': 34, + 'BLUE': 94, + 'BLUE_BG': 44, + 'LIGHT_BLUE_BG': 104, + 'DARK_MAGENTA': 35, + 'PURPLE': 95, + 'MAGENTA_BG': 45, + 'LIGHT_PURPLE_BG': 105, + 'DARK_CYAN': 36, + 'AUQA': 96, + 'AQUA': 96, + 'CYAN_BG': 46, + 'LIGHT_AUQA_BG': 106, + 'LIGHT_AQUA_BG': 106, + 'DARK_GREEN': 32, + 'GREEN': 92, + 'GREEN_BG': 42, + 'LIGHT_GREEN_BG': 102, + 'BLACK': 30, +} + + +# ----------------------------------------------------------------------------- +def termcode(num): + """ + Output of an ANSII terminal code. + """ + + return('\033[%sm' % (num)) + + +# ----------------------------------------------------------------------------- +def colorstr(message, color): + """ + Wrapper function to colorize the message. + + @param message: The message to colorize + @type message: str + @param color: The color to use, must be one of the keys of COLOR_CODE + @type color: str + + @return: the colorized message + @rtype: str + + """ + + tcode = '' + if isinstance(color, (list, tuple)): + for clr in color: + tcode += termcode(COLOR_CODE[clr]) + else: + tcode = termcode(COLOR_CODE[color]) + + return tcode + message + termcode(COLOR_CODE['ENDC']) + +LOG = logging.getLogger(__name__) + + +# ============================================================================= +class ColoredFormatter(logging.Formatter): + """ + A variant of code found at: + http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored + """ + + LEVEL_COLOR = { + 'DEBUG': None, + 'INFO': 'GREEN', + 'WARNING': 'YELLOW', + 'ERROR': ('BOLD', 'RED'), + 'CRITICAL': 'RED_BG', + } + + # ------------------------------------------------------------------------- + def __init__(self, fmt=None, datefmt=None): + """ + Initialize the formatter with specified format strings. + + Initialize the formatter either with the specified format string, or a + default. Allow for specialized date formatting with the optional + datefmt argument (if omitted, you get the ISO8601 format). + """ + + logging.Formatter.__init__(self, fmt, datefmt) + + # ----------------------------------------------------------- + @property + def color_debug(self): + """The color used to output debug messages.""" + return self.LEVEL_COLOR['DEBUG'] + + @color_debug.setter + def color_debug(self, value): + self.LEVEL_COLOR['DEBUG'] = value + + # ----------------------------------------------------------- + @property + def color_info(self): + """The color used to output info messages.""" + return self.LEVEL_COLOR['INFO'] + + @color_info.setter + def color_info(self, value): + self.LEVEL_COLOR['INFO'] = value + + # ----------------------------------------------------------- + @property + def color_warning(self): + """The color used to output warning messages.""" + return self.LEVEL_COLOR['WARNING'] + + @color_warning.setter + def color_warning(self, value): + self.LEVEL_COLOR['WARNING'] = value + + # ----------------------------------------------------------- + @property + def color_error(self): + """The color used to output error messages.""" + return self.LEVEL_COLOR['ERROR'] + + @color_error.setter + def color_error(self, value): + self.LEVEL_COLOR['ERROR'] = value + + # ----------------------------------------------------------- + @property + def color_critical(self): + """The color used to output critical messages.""" + return self.LEVEL_COLOR['CRITICAL'] + + @color_critical.setter + def color_critical(self, value): + self.LEVEL_COLOR['CRITICAL'] = value + + # ------------------------------------------------------------------------- + def format(self, record): + """ + Format the specified record as text. + """ + + record = copy.copy(record) + levelname = record.levelname + + if levelname in self.LEVEL_COLOR: + + record.name = colorstr(record.name, 'BOLD') + record.filename = colorstr(record.filename, 'BOLD') + record.module = colorstr(record.module, 'BOLD') + record.funcName = colorstr(record.funcName, 'BOLD') + record.pathname = colorstr(record.pathname, 'BOLD') + record.processName = colorstr(record.processName, 'BOLD') + record.threadName = colorstr(record.threadName, 'BOLD') + + if self.LEVEL_COLOR[levelname] is not None: + record.levelname = colorstr( + levelname, self.LEVEL_COLOR[levelname]) + record.msg = colorstr(record.msg, self.LEVEL_COLOR[levelname]) + + return logging.Formatter.format(self, record) + + +# ============================================================================= + +if __name__ == "__main__": + pass + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/pp_lib/common.py b/pp_lib/common.py new file mode 100644 index 0000000..a4180e3 --- /dev/null +++ b/pp_lib/common.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@author: Frank Brehm +@contact: frank.brehm@pixelpark.com +@copyright: © 2017 by Frank Brehm, Berlin +@summary: The module for common used functions. +""" + +# Standard modules +import sys +import os +import logging +import re +import pprint +import platform + +# Third party modules +import six + +# Own modules + +__version__ = '0.2.1' + +log = logging.getLogger(__name__) + +# ============================================================================= +def pp(value, indent=4, width=99, depth=None): + """ + Returns a pretty print string of the given value. + + @return: pretty print string + @rtype: str + """ + + pretty_printer = pprint.PrettyPrinter( + indent=indent, width=width, depth=depth) + return pretty_printer.pformat(value) + + +# ============================================================================= +def terminal_can_colors(debug=False): + """ + Method to detect, whether the current terminal (stdout and stderr) + is able to perform ANSI color sequences. + + @return: both stdout and stderr can perform ANSI color sequences + @rtype: bool + + """ + + cur_term = '' + if 'TERM' in os.environ: + cur_term = os.environ['TERM'].lower().strip() + + colored_term_list = ( + r'ansi', + r'linux.*', + r'screen.*', + r'[xeak]term.*', + r'gnome.*', + r'rxvt.*', + r'interix', + ) + term_pattern = r'^(?:' + r'|'.join(colored_term_list) + r')$' + re_term = re.compile(term_pattern) + + ansi_term = False + env_term_has_colors = False + + if cur_term: + if cur_term == 'ansi': + env_term_has_colors = True + ansi_term = True + elif re_term.search(cur_term): + env_term_has_colors = True + if debug: + sys.stderr.write( + "ansi_term: %r, env_term_has_colors: %r\n" % ( + ansi_term, env_term_has_colors)) + + has_colors = False + if env_term_has_colors: + has_colors = True + for handle in [sys.stdout, sys.stderr]: + if (hasattr(handle, "isatty") and handle.isatty()): + if debug: + sys.stderr.write("%s is a tty.\n" % (handle.name)) + if (platform.system() == 'Windows' and not ansi_term): + if debug: + sys.stderr.write("platform is Windows and not ansi_term.\n") + has_colors = False + else: + if debug: + sys.stderr.write("%s is not a tty.\n" % (handle.name)) + if ansi_term: + pass + else: + has_colors = False + + return has_colors + + +# ============================================================================= +def to_unicode(obj, encoding='utf-8'): + + do_decode = False + if six.PY2: + if isinstance(obj, str): + do_decode = True + else: + if isinstance(obj, bytes): + do_decode = True + + if do_decode: + obj = obj.decode(encoding) + + return obj + + +# ============================================================================= +def to_utf8(obj): + + return encode_or_bust(obj, 'utf-8') + + +# ============================================================================= +def encode_or_bust(obj, encoding='utf-8'): + + do_encode = False + if six.PY2: + if isinstance(obj, unicode): + do_encode = True + else: + if isinstance(obj, str): + do_encode = True + + if do_encode: + obj = obj.encode(encoding) + + return obj + + +# ============================================================================= +def to_bytes(obj, encoding='utf-8'): + "Wrapper for encode_or_bust()" + + return encode_or_bust(obj, encoding) + + +# ============================================================================= +def to_str(obj, encoding='utf-8'): + """ + Transformes the given string-like object into the str-type according + to the current Python version. + """ + + if six.PY2: + return encode_or_bust(obj, encoding) + else: + return to_unicode_or_bust(obj, encoding) + + +# ============================================================================= + +if __name__ == "__main__": + + pass + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/pp_lib/errors.py b/pp_lib/errors.py new file mode 100644 index 0000000..481af30 --- /dev/null +++ b/pp_lib/errors.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@author: Frank Brehm +@summary: module for some common used error classes +""" + +# Standard modules +import errno + + +__version__ = '0.2.2' + +# ============================================================================= +class PpError(Exception): + """ + Base error class for all other self defined exceptions. + """ + + pass + + +# ============================================================================= +class PpAppError(PpError): + + pass + + +# ============================================================================= +class InvalidMailAddressError(PpError): + """Class for a exception in case of a malformed mail address.""" + + # ------------------------------------------------------------------------- + def __init__(self, address, msg=None): + + self.address = address + self.msg = msg + + # ------------------------------------------------------------------------- + def __str__(self): + + msg = "Wrong mail address {a!r} ({c})".format( + a=self.address, c=self.address.__class__.__name__) + if self.msg: + msg += ': ' + self.msg + else: + msg += '.' + return msg + + +# ============================================================================= + +if __name__ == "__main__": + pass + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/pp_lib/mailaddress.py b/pp_lib/mailaddress.py new file mode 100644 index 0000000..09b5090 --- /dev/null +++ b/pp_lib/mailaddress.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@author: Frank Brehm +@contact: frank.brehm@pixelpark.com +@copyright: © 2010 - 2016 by Frank Brehm, ProfitBricks GmbH, Berlin +@summary: The module for the MailAddress object. +""" + +# Standard modules +import sys +import os +import logging +import re + +# Own modules +from pp_lib.errors import InvalidMailAddressError + +from pp_lib.common import to_str + +__version__ = '0.2.2' +log = logging.getLogger(__name__) + + +# ============================================================================= +class MailAddress(object): + """ + Class for encapsulating a mail simple address. + """ + + pattern_valid_domain = r'@((?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)+[a-z][a-z]+)$' + + pattern_valid_user = r'^([a-z0-9][a-z0-9_\-\.\+\&@]*[a-z0-9]' + pattern_valid_user += r'(?:\+[a-z0-9][a-z0-9_\-\.]*[a-z0-9])*)' + + pattern_valid_address = pattern_valid_user + pattern_valid_domain + + re_valid_user = re.compile(pattern_valid_user + r'$', re.IGNORECASE) + re_valid_domain = re.compile(r'^' + pattern_valid_domain, re.IGNORECASE) + re_valid_address = re.compile(pattern_valid_address, re.IGNORECASE) + + verbose = 0 + + # ------------------------------------------------------------------------- + @classmethod + def valid_address(cls, address, raise_on_failure=False): + + if not address: + e = InvalidMailAddressError(address, "Empty address.") + if raise_on_failure: + raise e + elif cls.verbose > 2: + log.debug(str(e)) + return False + + addr = to_str(address) + if not isinstance(addr, str): + e = InvalidMailAddressError(address, "Wrong type.") + if raise_on_failure: + raise e + elif cls.verbose > 2: + log.debug(str(e)) + return False + + if cls.re_valid_address.search(addr): + return True + + e = InvalidMailAddressError(address, "Invalid address.") + if raise_on_failure: + raise e + elif cls.verbose > 2: + log.debug(str(e)) + return False + + # ------------------------------------------------------------------------- + def __init__(self, user=None, domain=None): + + self._user = '' + self._domain = '' + + if not domain: + if user: + addr = to_str(user) + if self.valid_address(addr): + match = self.re_valid_address.search(addr) + self._user = match.group(1) + self._domain = match.group(2) + return + match = self.re_valid_domain.search(addr) + if match: + self._domain = match.group(1) + return + self._user = addr + return + + self._user = to_str(user) + self._domain = to_str(domain) + + # ----------------------------------------------------------- + @property + def user(self): + """The user part of the address.""" + if self._user is None: + return '' + return self._user + + # ----------------------------------------------------------- + @property + def domain(self): + """The domain part of the address.""" + if self._domain is None: + return '' + return self._domain + + # ------------------------------------------------------------------------- + def __str__(self): + + if not self.user and not self.domain: + return '' + + if not self.domain: + return self.user + + if not self.user: + return '@' + self.domain + + return self.user + '@' + self.domain + + # ------------------------------------------------------------------------- + def str_for_access(self): + + if not self.user and not self.domain: + return None + + if not self.domain: + return self.user + '@' + + if not self.user: + return self.domain + + return self.user + '@' + self.domain + + # ------------------------------------------------------------------------- + def __repr__(self): + """Typecasting into a string for reproduction.""" + + out = "<%s(" % (self.__class__.__name__) + + fields = [] + fields.append("user={!r}".format(self.user)) + fields.append("domain={!r}".format(self.domain)) + + out += ", ".join(fields) + ")>" + return out + + + # ------------------------------------------------------------------------- + def __hash__(self): + return hash(str(self).lower()) + + # ------------------------------------------------------------------------- + def __eq__(self, other): + + if not isinstance(other, MailAddress): + if other is None: + return False + return str(self).lower() == str(other).lower() + + if not self.user: + if other.user: + return False + if not self.domain: + if other.domain: + return False + return True + if not other.domain: + return False + if self.domain.lower() == other.domain.lower(): + return True + return False + + if not self.domain: + if other.domain: + return False + if not other.user: + return False + if self.user.lower() == other.user.lower(): + return True + return False + + if not other.user: + return False + if not other.domain: + return False + if self.domain.lower() != other.domain.lower(): + return False + if self.user.lower() != other.user.lower(): + return False + + return True + + # ------------------------------------------------------------------------- + def __ne__(self, other): + + if self == other: + return False + return True + + # ------------------------------------------------------------------------- + def __lt__(self, other): + + if not isinstance(other, MailAddress): + if other is None: + return False + return str(self).lower() < str(other).lower() + + if not self.user: + if not self.domain: + if other.domain: + return False + return True + if not other.domain: + return False + if self.domain.lower() != other.domain.lower(): + return self.domain.lower() < other.domain.lower() + if other.user: + return False + return True + + if not self.domain: + if other.domain: + return True + if not other.user: + return False + if self.user.lower() != other.user.lower(): + return self.user.lower() < other.user.lower() + return False + + if not other.domain: + return False + if not other.user: + return False + + if self.domain.lower() != other.domain.lower(): + return self.domain.lower() < other.domain.lower() + if self.user.lower() != other.user.lower(): + return self.user.lower() < other.user.lower() + + return False + + # ------------------------------------------------------------------------- + def __gt__(self, other): + + if not isinstance(other, MailAddress): + return NotImplemented + + if self < other: + return False + return True + + # ------------------------------------------------------------------------- + def __copy__(self): + "Implementing a wrapper for copy.copy()." + + addr = MailAddress() + addr._user = self.user + addr._domain = self.domain + return addr + + +# ============================================================================= + +if __name__ == "__main__": + + pass + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list