# -*- coding: utf-8 -*- # ***************************************************************************** # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program 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 General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Module authors: # Enrico Faulhaber # # ***************************************************************************** """Define helpers""" import importlib import linecache import socket import sys import threading import traceback from configparser import ConfigParser from os import environ, path CONFIG = {} unset_value = object() def getGeneralConfig(confdir=None): global CONFIG # pylint: disable=global-statement if CONFIG: if confdir: raise ValueError('getGeneralConfig with argument must be called first') else: repodir = path.abspath(path.join(path.dirname(__file__), '..', '..')) if path.splitext(sys.executable)[1] == ".exe" and not path.basename(sys.executable).startswith('python'): # special MS windows environment CONFIG = { 'piddir': './', 'logdir': './log', 'confdir': './', } elif not path.exists(path.join(repodir, '.git')): CONFIG = { 'piddir': '/var/run/secop', 'logdir': '/var/log', 'confdir': '/etc/secop', } else: CONFIG = { 'piddir': path.join(repodir, 'pid'), 'logdir': path.join(repodir, 'log'), 'confdir': path.join(repodir, 'cfg'), } gen_config_path = confdir or environ.get('FRAPPY_CONFIG_FILE', path.join(CONFIG['confdir'], 'generalConfig.cfg')) if gen_config_path and path.exists(gen_config_path): parser = ConfigParser() parser.optionxform = str parser.read([gen_config_path]) CONFIG = {} # only the FRAPPY section is relevant, other sections might be used by others for key, value in parser['FRAPPY'].items(): if value.startswith('./'): CONFIG[key] = path.abspath(path.join(repodir, value)) else: # expand ~ to username, also in path lists separated with ':' CONFIG[key] = ':'.join(path.expanduser(v) for v in value.split(':')) else: for dirname in CONFIG: CONFIG[dirname] = environ.get('SECOP_%s' % dirname.upper(), CONFIG[dirname]) # this is not customizable CONFIG['basedir'] = repodir return CONFIG class lazy_property: """A property that calculates its value only once.""" def __init__(self, func): self._func = func self.__name__ = func.__name__ self.__doc__ = func.__doc__ def __get__(self, obj, obj_class): if obj is None: return self obj.__dict__[self.__name__] = self._func(obj) return obj.__dict__[self.__name__] class attrdict(dict): """a normal dict, providing access also via attributes""" def __getattr__(self, key): return self[key] def __setattr__(self, key, value): self[key] = value def clamp(_min, value, _max): """return the median of 3 values, i.e. value if min <= value <= max, else min or max depending on which side value lies outside the [min..max] interval. This works even when min > max! """ # return median, i.e. clamp the the value between min and max return sorted([_min, value, _max])[1] def get_class(spec): """loads a class given by string in dotted notation (as python would do)""" modname, classname = spec.rsplit('.', 1) if modname.startswith('secop'): module = importlib.import_module(modname) else: # rarely needed by now.... module = importlib.import_module('secop.' + modname) try: return getattr(module, classname) except AttributeError: raise AttributeError('no such class') from None def mkthread(func, *args, **kwds): t = threading.Thread( name='%s:%s' % (func.__module__, func.__name__), target=func, args=args, kwargs=kwds) t.daemon = True t.start() return t def formatExtendedFrame(frame): ret = [] for key, value in frame.f_locals.items(): try: valstr = repr(value)[:256] except Exception: valstr = '' ret.append(' %-20s = %s\n' % (key, valstr)) ret.append('\n') return ret def formatExtendedTraceback(exc_info=None): if exc_info is None: etype, value, tb = sys.exc_info() else: etype, value, tb = exc_info ret = ['Traceback (most recent call last):\n'] while tb is not None: frame = tb.tb_frame filename = frame.f_code.co_filename item = ' File "%s", line %d, in %s\n' % (filename, tb.tb_lineno, frame.f_code.co_name) linecache.checkcache(filename) line = linecache.getline(filename, tb.tb_lineno, frame.f_globals) if line: item = item + ' %s\n' % line.strip() ret.append(item) if filename not in ('