# -*- coding: utf8 -*- __doc__ = """GNUmed general tools.""" #=========================================================================== __author__ = "K. Hilbert " __license__ = "GPL v2 or later (details at http://www.gnu.org)" # std libs import re as regex, sys, os, os.path, csv, tempfile, logging, hashlib import decimal import cPickle, zlib import xml.sax.saxutils as xml_tools # GNUmed libs if __name__ == '__main__': # for testing: logging.basicConfig(level = logging.DEBUG) sys.path.insert(0, '../../') from Gnumed.pycommon import gmI18N gmI18N.activate_locale() gmI18N.install_domain() from Gnumed.pycommon import gmBorg _log = logging.getLogger('gm.tools') # CAPitalization modes: ( CAPS_NONE, # don't touch it CAPS_FIRST, # CAP first char, leave rest as is CAPS_ALLCAPS, # CAP all chars CAPS_WORDS, # CAP first char of every word CAPS_NAMES, # CAP in a way suitable for names (tries to be smart) CAPS_FIRST_ONLY # CAP first char, lowercase the rest ) = range(6) u_currency_pound = u'\u00A3' # Pound sign u_currency_yen = u'\u00A5' # Yen sign u_right_double_angle_quote = u'\u00AB' # << u_registered_trademark = u'\u00AE' u_plus_minus = u'\u00B1' u_left_double_angle_quote = u'\u00BB' # >> u_one_quarter = u'\u00BC' u_one_half = u'\u00BD' u_three_quarters = u'\u00BE' u_multiply = u'\u00D7' # x u_ellipsis = u'\u2026' # ... u_euro = u'\u20AC' # EURO sign u_numero = u'\u2116' # No. / # sign u_down_left_arrow = u'\u21B5' # <-' u_left_arrow = u'\u2190' # <-- u_right_arrow = u'\u2192' # --> u_left_arrow_with_tail = u'\u21a2' # <--< u_sum = u'\u2211' u_almost_equal_to = u'\u2248' # approximately / nearly / roughly u_corresponds_to = u'\u2258' u_infinity = u'\u221E' u_diameter = u'\u2300' u_checkmark_crossed_out = u'\u237B' u_box_horiz_single = u'\u2500' u_box_horiz_4dashes = u'\u2508' u_box_top_double = u'\u2550' u_box_top_left_double_single = u'\u2552' u_box_top_right_double_single = u'\u2555' u_box_top_left_arc = u'\u256d' u_box_bottom_right_arc = u'\u256f' u_box_bottom_left_arc = u'\u2570' u_box_horiz_light_heavy = u'\u257c' u_box_horiz_heavy_light = u'\u257e' u_skull_and_crossbones = u'\u2620' u_frowning_face = u'\u2639' u_smiling_face = u'\u263a' u_black_heart = u'\u2665' u_checkmark_thin = u'\u2713' u_checkmark_thick = u'\u2714' u_writing_hand = u'\u270d' u_pencil_1 = u'\u270e' u_pencil_2 = u'\u270f' u_pencil_3 = u'\u2710' u_latin_cross = u'\u271d' u_kanji_yen = u'\u5186' # Yen kanji u_replacement_character = u'\ufffd' u_link_symbol = u'\u1f517' #=========================================================================== def handle_uncaught_exception_console(t, v, tb): print ".========================================================" print "| Unhandled exception caught !" print "| Type :", t print "| Value:", v print "`========================================================" _log.critical('unhandled exception caught', exc_info = (t,v,tb)) sys.__excepthook__(t,v,tb) #=========================================================================== # path level operations #--------------------------------------------------------------------------- def mkdir(directory=None): try: os.makedirs(directory) except OSError, e: if (e.errno == 17) and not os.path.isdir(directory): raise return True #--------------------------------------------------------------------------- class gmPaths(gmBorg.cBorg): """This class provides the following paths: .home_dir .local_base_dir .working_dir .user_config_dir .system_config_dir .system_app_data_dir .tmp_dir (readonly) """ def __init__(self, app_name=None, wx=None): """Setup pathes. will default to (name of the script - .py) """ try: self.already_inited return except AttributeError: pass self.init_paths(app_name=app_name, wx=wx) self.already_inited = True #-------------------------------------- # public API #-------------------------------------- def init_paths(self, app_name=None, wx=None): if wx is None: _log.debug('wxPython not available') _log.debug('detecting paths directly') if app_name is None: app_name, ext = os.path.splitext(os.path.basename(sys.argv[0])) _log.info('app name detected as [%s]', app_name) else: _log.info('app name passed in as [%s]', app_name) # the user home, doesn't work in Wine so work around that self.__home_dir = None # where the main script (the "binary") is installed if getattr(sys, 'frozen', False): _log.info('frozen app, installed into temporary path') # this would find the path of *THIS* file #self.local_base_dir = os.path.dirname(__file__) # while this is documented on the web, the ${_MEIPASS2} does not exist #self.local_base_dir = os.environ.get('_MEIPASS2') # this is what Martin Zibricky told us to use # when asking about this on address@hidden #self.local_base_dir = sys._MEIPASS # however, we are --onedir, so we should look at sys.executable # as per the pyinstaller manual self.local_base_dir = os.path.dirname(sys.executable) else: self.local_base_dir = os.path.abspath(os.path.dirname(sys.argv[0])) # the current working dir at the OS self.working_dir = os.path.abspath(os.curdir) # user-specific config dir, usually below the home dir mkdir(os.path.join(self.home_dir, '.%s' % app_name)) self.user_config_dir = os.path.join(self.home_dir, '.%s' % app_name) # system-wide config dir, usually below /etc/ under UN*X try: self.system_config_dir = os.path.join('/etc', app_name) except ValueError: #self.system_config_dir = self.local_base_dir self.system_config_dir = self.user_config_dir # system-wide application data dir try: self.system_app_data_dir = os.path.join(sys.prefix, 'share', app_name) except ValueError: self.system_app_data_dir = self.local_base_dir # temporary directory try: self.__tmp_dir_already_set _log.debug('temp dir already set') except AttributeError: tmp_base = os.path.join(tempfile.gettempdir(), app_name) mkdir(tmp_base) _log.info('previous temp dir: %s', tempfile.gettempdir()) tempfile.tempdir = tmp_base _log.info('intermediate temp dir: %s', tempfile.gettempdir()) self.tmp_dir = tempfile.mkdtemp(prefix = r'gm-') self.__log_paths() if wx is None: return True # retry with wxPython _log.debug('re-detecting paths with wxPython') std_paths = wx.StandardPaths.Get() _log.info('wxPython app name is [%s]', wx.GetApp().GetAppName()) # user-specific config dir, usually below the home dir mkdir(os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name)) self.user_config_dir = os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name) # system-wide config dir, usually below /etc/ under UN*X try: tmp = std_paths.GetConfigDir() if not tmp.endswith(app_name): tmp = os.path.join(tmp, app_name) self.system_config_dir = tmp except ValueError: # leave it at what it was from direct detection pass # system-wide application data dir # Robin attests that the following doesn't always # give sane values on Windows, so IFDEF it if 'wxMSW' in wx.PlatformInfo: _log.warning('this platform (wxMSW) sometimes returns a broken value for the system-wide application data dir') else: try: self.system_app_data_dir = std_paths.GetDataDir() except ValueError: pass self.__log_paths() return True #-------------------------------------- def __log_paths(self): _log.debug('sys.argv[0]: %s', sys.argv[0]) _log.debug('sys.executable: %s', sys.executable) _log.debug('sys._MEIPASS: %s', getattr(sys, '_MEIPASS', '')) _log.debug('os.environ["_MEIPASS2"]: %s', os.environ.get('_MEIPASS2', '')) _log.debug('__file__ : %s', __file__) _log.debug('local application base dir: %s', self.local_base_dir) _log.debug('current working dir: %s', self.working_dir) _log.debug('user home dir: %s', self.home_dir) _log.debug('user-specific config dir: %s', self.user_config_dir) _log.debug('system-wide config dir: %s', self.system_config_dir) _log.debug('system-wide application data dir: %s', self.system_app_data_dir) _log.debug('temporary dir: %s', self.tmp_dir) #-------------------------------------- # properties #-------------------------------------- def _set_user_config_dir(self, path): if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)): msg = '[%s:user_config_dir]: invalid path [%s]' % (self.__class__.__name__, path) _log.error(msg) raise ValueError(msg) self.__user_config_dir = path def _get_user_config_dir(self): return self.__user_config_dir user_config_dir = property(_get_user_config_dir, _set_user_config_dir) #-------------------------------------- def _set_system_config_dir(self, path): if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)): msg = '[%s:system_config_dir]: invalid path [%s]' % (self.__class__.__name__, path) _log.error(msg) raise ValueError(msg) self.__system_config_dir = path def _get_system_config_dir(self): return self.__system_config_dir system_config_dir = property(_get_system_config_dir, _set_system_config_dir) #-------------------------------------- def _set_system_app_data_dir(self, path): if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)): msg = '[%s:system_app_data_dir]: invalid path [%s]' % (self.__class__.__name__, path) _log.error(msg) raise ValueError(msg) self.__system_app_data_dir = path def _get_system_app_data_dir(self): return self.__system_app_data_dir system_app_data_dir = property(_get_system_app_data_dir, _set_system_app_data_dir) #-------------------------------------- def _set_home_dir(self, path): raise ValueError('invalid to set home dir') def _get_home_dir(self): if self.__home_dir is not None: return self.__home_dir tmp = os.path.expanduser('~') if tmp == '~': _log.error('this platform does not expand ~ properly') try: tmp = os.environ['USERPROFILE'] except KeyError: _log.error('cannot access $USERPROFILE in environment') if not ( os.access(tmp, os.R_OK) and os.access(tmp, os.X_OK) and os.access(tmp, os.W_OK) ): msg = '[%s:home_dir]: invalid path [%s]' % (self.__class__.__name__, tmp) _log.error(msg) raise ValueError(msg) self.__home_dir = tmp return self.__home_dir home_dir = property(_get_home_dir, _set_home_dir) #-------------------------------------- def _set_tmp_dir(self, path): if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)): msg = '[%s:tmp_dir]: invalid path [%s]' % (self.__class__.__name__, path) _log.error(msg) raise ValueError(msg) _log.debug('previous temp dir: %s', tempfile.gettempdir()) self.__tmp_dir = path tempfile.tempdir = self.__tmp_dir self.__tmp_dir_already_set = True def _get_tmp_dir(self): return self.__tmp_dir tmp_dir = property(_get_tmp_dir, _set_tmp_dir) #=========================================================================== # file related tools #--------------------------------------------------------------------------- def file2md5(filename=None, return_hex=True): blocksize = 2**10 * 128 # 128k, since md5 use 128 byte blocks _log.debug('md5(%s): <%s> byte blocks', filename, blocksize) f = open(filename, 'rb') md5 = hashlib.md5() while True: data = f.read(blocksize) if not data: break md5.update(data) _log.debug('md5(%s): %s', filename, md5.hexdigest()) if return_hex: return md5.hexdigest() return md5.digest() #--------------------------------------------------------------------------- def unicode2charset_encoder(unicode_csv_data, encoding='utf-8'): for line in unicode_csv_data: yield line.encode(encoding) #def utf_8_encoder(unicode_csv_data): # for line in unicode_csv_data: # yield line.encode('utf-8') default_csv_reader_rest_key = u'list_of_values_of_unknown_fields' def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, encoding='utf-8', **kwargs): # csv.py doesn't do Unicode; encode temporarily as UTF-8: try: is_dict_reader = kwargs['dict'] del kwargs['dict'] if is_dict_reader is not True: raise KeyError kwargs['restkey'] = default_csv_reader_rest_key csv_reader = csv.DictReader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs) except KeyError: is_dict_reader = False csv_reader = csv.reader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs) for row in csv_reader: # decode ENCODING back to Unicode, cell by cell: if is_dict_reader: for key in row.keys(): if key == default_csv_reader_rest_key: old_data = row[key] new_data = [] for val in old_data: new_data.append(unicode(val, encoding)) row[key] = new_data if default_csv_reader_rest_key not in csv_reader.fieldnames: csv_reader.fieldnames.append(default_csv_reader_rest_key) else: row[key] = unicode(row[key], encoding) yield row else: yield [ unicode(cell, encoding) for cell in row ] #yield [unicode(cell, 'utf-8') for cell in row] #--------------------------------------------------------------------------- def get_unique_filename(prefix=None, suffix=None, tmp_dir=None): """This introduces a race condition between the file.close() and actually using the filename. The file will not exist after calling this function. """ if tmp_dir is not None: if ( not os.access(tmp_dir, os.F_OK) or not os.access(tmp_dir, os.X_OK | os.W_OK) ): _log.info('cannot find temporary dir [%s], using system default', tmp_dir) tmp_dir = None kwargs = {'dir': tmp_dir} if prefix is None: kwargs['prefix'] = 'gnumed-' else: kwargs['prefix'] = prefix if suffix in [None, u'']: kwargs['suffix'] = '.tmp' else: if not suffix.startswith('.'): suffix = '.' + suffix kwargs['suffix'] = suffix f = tempfile.NamedTemporaryFile(**kwargs) filename = f.name f.close() return filename #=========================================================================== def import_module_from_directory(module_path=None, module_name=None, always_remove_path=False): """Import a module from any location.""" remove_path = always_remove_path or False if module_path not in sys.path: _log.info('appending to sys.path: [%s]' % module_path) sys.path.append(module_path) remove_path = True _log.debug('will remove import path: %s', remove_path) if module_name.endswith('.py'): module_name = module_name[:-3] try: module = __import__(module_name) except StandardError: _log.exception('cannot __import__() module [%s] from [%s]' % (module_name, module_path)) while module_path in sys.path: sys.path.remove(module_path) raise _log.info('imported module [%s] as [%s]' % (module_name, module)) if remove_path: while module_path in sys.path: sys.path.remove(module_path) return module #=========================================================================== # text related tools #--------------------------------------------------------------------------- _kB = 1024 _MB = 1024 * _kB _GB = 1024 * _MB _TB = 1024 * _GB _PB = 1024 * _TB #--------------------------------------------------------------------------- def size2str(size=0, template=u'%s'): if size == 1: return template % _('1 Byte') if size < 10 * _kB: return template % _('%s Bytes') % size if size < _MB: return template % u'%.1f kB' % (float(size) / _kB) if size < _GB: return template % u'%.1f MB' % (float(size) / _MB) if size < _TB: return template % u'%.1f GB' % (float(size) / _GB) if size < _PB: return template % u'%.1f TB' % (float(size) / _TB) return template % u'%.1f PB' % (float(size) / _PB) #--------------------------------------------------------------------------- def bool2subst(boolean=None, true_return=True, false_return=False, none_return=None): if boolean is None: return none_return if boolean is True: return true_return if boolean is False: return false_return raise ValueError('bool2subst(): arg must be either of True, False, None') #--------------------------------------------------------------------------- def bool2str(boolean=None, true_str='True', false_str='False'): return bool2subst ( boolean = bool(boolean), true_return = true_str, false_return = false_str ) #--------------------------------------------------------------------------- def none_if(value=None, none_equivalent=None, strip_string=False): """Modelled after the SQL NULLIF function.""" if value is None: return None if strip_string: stripped = value.strip() else: stripped = value if stripped == none_equivalent: return None return value #--------------------------------------------------------------------------- def coalesce(initial=None, instead=None, template_initial=None, template_instead=None, none_equivalents=None, function_initial=None): """Modelled after the SQL coalesce function. To be used to simplify constructs like: if initial is None (or in none_equivalents): real_value = (template_instead % instead) or instead else: real_value = (template_initial % initial) or initial print real_value @param initial: the value to be tested for @type initial: any Python type, must have a __str__ method if template_initial is not None @param instead: the value to be returned if is None @type instead: any Python type, must have a __str__ method if template_instead is not None @param template_initial: if is returned replace the value into this template, must contain one <%s> @type template_initial: string or None @param template_instead: if is returned replace the value into this template, must contain one <%s> @type template_instead: string or None example: function_initial = ('strftime', '%Y-%m-%d') Ideas: - list of insteads: initial, [instead, template], [instead, template], [instead, template], template_initial, ... """ if none_equivalents is None: none_equivalents = [None] if initial in none_equivalents: if template_instead is None: return instead return template_instead % instead if function_initial is not None: funcname, args = function_initial func = getattr(initial, funcname) initial = func(args) if template_initial is None: return initial try: return template_initial % initial except TypeError: return template_initial #--------------------------------------------------------------------------- def __cap_name(match_obj=None): val = match_obj.group(0).lower() if val in ['von', 'van', 'de', 'la', 'l', 'der', 'den']: # FIXME: this needs to expand, configurable ? return val buf = list(val) buf[0] = buf[0].upper() for part in ['mac', 'mc', 'de', 'la']: if len(val) > len(part) and val[:len(part)] == part: buf[len(part)] = buf[len(part)].upper() return ''.join(buf) #--------------------------------------------------------------------------- def capitalize(text=None, mode=CAPS_NAMES): """Capitalize the first character but leave the rest alone. Note that we must be careful about the locale, this may have issues ! However, for UTF strings it should just work. """ if (mode is None) or (mode == CAPS_NONE): return text if mode == CAPS_FIRST: if len(text) == 1: return text[0].upper() return text[0].upper() + text[1:] if mode == CAPS_ALLCAPS: return text.upper() if mode == CAPS_FIRST_ONLY: if len(text) == 1: return text[0].upper() return text[0].upper() + text[1:].lower() if mode == CAPS_WORDS: return regex.sub(ur'(\w)(\w+)', lambda x: x.group(1).upper() + x.group(2).lower(), text) if mode == CAPS_NAMES: #return regex.sub(r'\w+', __cap_name, text) return capitalize(text=text, mode=CAPS_FIRST) # until fixed print "ERROR: invalid capitalization mode: [%s], leaving input as is" % mode return text #--------------------------------------------------------------------------- def input2decimal(initial=None): if isinstance(initial, decimal.Decimal): return True, initial val = initial # float ? -> to string first if type(val) == type(float(1.4)): val = str(val) # string ? -> "," to "." if isinstance(val, basestring): val = val.replace(',', '.', 1) val = val.strip() try: d = decimal.Decimal(val) return True, d except (TypeError, decimal.InvalidOperation): return False, val #--------------------------------------------------------------------------- def input2int(initial=None, minval=None, maxval=None): val = initial # string ? -> "," to "." if isinstance(val, basestring): val = val.replace(',', '.', 1) val = val.strip() try: int_val = int(val) except (TypeError, ValueError): _log.exception('int(%s) failed', val) return False, val if minval is not None: if int_val < minval: _log.debug('%s < min (%s)', val, minval) return False, val if maxval is not None: if int_val > maxval: _log.debug('%s > max (%s)', val, maxval) return False, val return True, int_val #--------------------------------------------------------------------------- def strip_leading_empty_lines(lines=None, text=None, eol=u'\n'): return_join = False if lines is None: return_join = True lines = eol.split(text) while True: if lines[0].strip(eol).strip() != u'': break lines = lines[1:] if return_join: return eol.join(lines) return lines #--------------------------------------------------------------------------- def strip_trailing_empty_lines(lines=None, text=None, eol=u'\n'): return_join = False if lines is None: return_join = True lines = eol.split(text) while True: if lines[-1].strip(eol).strip() != u'': break lines = lines[:-1] if return_join: return eol.join(lines) return lines #--------------------------------------------------------------------------- def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'): """A word-wrap function that preserves existing line breaks and most spaces in the text. Expects that existing line breaks are posix newlines (\n). """ wrapped = initial_indent + reduce ( lambda line, word, width=width: '%s%s%s' % ( line, ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)], word ), text.split(' ') ) if subsequent_indent != u'': wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n')) if eol != u'\n': wrapped = wrapped.replace('\n', eol) return wrapped #--------------------------------------------------------------------------- def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '): text = text.replace(u'\r', u'') lines = text.split(u'\n') text = u'' for line in lines: if strip_whitespace: line = line.strip().strip(u'\t').strip() if remove_empty_lines: if line == u'': continue text += (u'%s%s' % (line, line_separator)) text = text.rstrip(line_separator) if max_length is not None: text = text[:max_length] text = text.rstrip(line_separator) return text #--------------------------------------------------------------------------- def xml_escape_string(text=None): """check for special XML characters and transform them""" return xml_tools.escape ( text, entities = { u'&': u'&' } ) # text = text.replace(u'&', u'&') # return text #--------------------------------------------------------------------------- def tex_escape_string(text=None): """check for special TeX characters and transform them""" text = text.replace(u'\\', u'\\textbackslash') text = text.replace(u'^', u'\\textasciicircum') text = text.replace('~','\\textasciitilde') text = text.replace(u'{', u'\\{') text = text.replace(u'}', u'\\}') text = text.replace(u'%', u'\\%') text = text.replace(u'&', u'\\&') text = text.replace(u'#', u'\\#') text = text.replace(u'$', u'\\$') text = text.replace(u'_', u'\\_') text = text.replace(u_euro, u'\\EUR') return text #--------------------------------------------------------------------------- def prompted_input(prompt=None, default=None): """Obtains entry from standard input. prompt: Prompt text to display in standard output default: Default value (for user to press enter only) CTRL-C: aborts and returns None """ if prompt is None: msg = u'(CTRL-C aborts)' else: msg = u'%s (CTRL-C aborts)' % prompt if default is None: msg = msg + u': ' else: msg = u'%s [%s]: ' % (msg, default) try: usr_input = raw_input(msg) except KeyboardInterrupt: return None if usr_input == '': return default return usr_input #=========================================================================== # image handling tools #--------------------------------------------------------------------------- # builtin (ugly but tried and true) fallback icon __icon_serpent = \ """x\xdae\x8f\xb1\x0e\x83 \x10\x86w\x9f\xe2\x92\x1blb\xf2\x07\x96\xeaH:0\xd6\ \xc1\x85\xd5\x98N5\xa5\xef?\xf5N\xd0\x8a\xdcA\xc2\xf7qw\x84\xdb\xfa\xb5\xcd\ \xd4\xda;\xc9\x1a\xc8\xb6\xcd<\xb5\xa0\x85\x1e\xeb\xbc\xbc7b!\xf6\xdeHl\x1c\ \x94\x073\xec<*\xf7\xbe\xf7\x99\x9d\xb21~\xe7.\xf5\x1f\x1c\xd3\xbdVlL\xc2\ \xcf\xf8ye\xd0\x00\x90\x0etH \x84\x80B\xaa\x8a\x88\x85\xc4(U\x9d$\xfeR;\xc5J\ \xa6\x01\xbbt9\xceR\xc8\x81e_$\x98\xb9\x9c\xa9\x8d,y\xa9t\xc8\xcf\x152\xe0x\ \xe9$\xf5\x07\x95\x0cD\x95t:\xb1\x92\xae\x9cI\xa8~\x84\x1f\xe0\xa3ec""" def get_icon(wx=None): paths = gmPaths(app_name = u'gnumed', wx = wx) candidates = [ os.path.join(paths.system_app_data_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'), os.path.join(paths.local_base_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'), os.path.join(paths.system_app_data_dir, 'bitmaps', 'serpent.png'), os.path.join(paths.local_base_dir, 'bitmaps', 'serpent.png') ] found_as = None for candidate in candidates: try: open(candidate, 'r').close() found_as = candidate break except IOError: _log.debug('icon not found in [%s]', candidate) if found_as is None: _log.warning('no icon file found, falling back to builtin (ugly) icon') icon_bmp_data = wx.BitmapFromXPMData(cPickle.loads(zlib.decompress(__icon_serpent))) icon.CopyFromBitmap(icon_bmp_data) else: _log.debug('icon found in [%s]', found_as) icon = wx.EmptyIcon() try: icon.LoadFile(found_as, wx.BITMAP_TYPE_ANY) #_PNG except AttributeError: _log.exception(u"this platform doesn't support wx.Icon().LoadFile()") return icon #=========================================================================== # main #--------------------------------------------------------------------------- if __name__ == '__main__': if len(sys.argv) < 2: sys.exit() if sys.argv[1] != 'test': sys.exit() #----------------------------------------------------------------------- def test_input2decimal(): tests = [ [None, False], ['', False], [' 0 ', True, 0], [0, True, 0], [0.0, True, 0], [.0, True, 0], ['0', True, 0], ['0.0', True, 0], ['0,0', True, 0], ['00.0', True, 0], ['.0', True, 0], [',0', True, 0], [0.1, True, decimal.Decimal('0.1')], [.01, True, decimal.Decimal('0.01')], ['0.1', True, decimal.Decimal('0.1')], ['0,1', True, decimal.Decimal('0.1')], ['00.1', True, decimal.Decimal('0.1')], ['.1', True, decimal.Decimal('0.1')], [',1', True, decimal.Decimal('0.1')], [1, True, 1], [1.0, True, 1], ['1', True, 1], ['1.', True, 1], ['1,', True, 1], ['1.0', True, 1], ['1,0', True, 1], ['01.0', True, 1], ['01,0', True, 1], [' 01, ', True, 1], [decimal.Decimal('1.1'), True, decimal.Decimal('1.1')] ] for test in tests: conversion_worked, result = input2decimal(initial = test[0]) expected2work = test[1] if conversion_worked: if expected2work: if result == test[2]: continue else: print "ERROR (conversion result wrong): >%s<, expected >%s<, got >%s<" % (test[0], test[2], result) else: print "ERROR (conversion worked but was expected to fail): >%s<, got >%s<" % (test[0], result) else: if not expected2work: continue else: print "ERROR (conversion failed but was expected to work): >%s<, expected >%s<" % (test[0], test[2]) #----------------------------------------------------------------------- def test_input2int(): print input2int(0) print input2int('0') print input2int(u'0', 0, 0) #----------------------------------------------------------------------- def test_coalesce(): import datetime as dt print coalesce(initial = dt.datetime.now(), template_initial = u'-- %s --', function_initial = ('strftime', u'%Y-%m-%d')) print 'testing coalesce()' print "------------------" tests = [ [None, 'something other than ', None, None, 'something other than '], ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'], ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'], ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'], [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'], [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None'] ] passed = True for test in tests: result = coalesce ( initial = test[0], instead = test[1], template_initial = test[2], template_instead = test[3] ) if result != test[4]: print "ERROR" print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3]) print "expected:", test[4] print "received:", result passed = False if passed: print "passed" else: print "failed" return passed #----------------------------------------------------------------------- def test_capitalize(): print 'testing capitalize() ...' success = True pairs = [ # [original, expected result, CAPS mode] [u'Boot', u'Boot', CAPS_FIRST_ONLY], [u'boot', u'Boot', CAPS_FIRST_ONLY], [u'booT', u'Boot', CAPS_FIRST_ONLY], [u'BoOt', u'Boot', CAPS_FIRST_ONLY], [u'boots-Schau', u'Boots-Schau', CAPS_WORDS], [u'boots-sChau', u'Boots-Schau', CAPS_WORDS], [u'boot camp', u'Boot Camp', CAPS_WORDS], [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES], [u'häkkönen', u'Häkkönen', CAPS_NAMES], [u'McBurney', u'McBurney', CAPS_NAMES], [u'mcBurney', u'McBurney', CAPS_NAMES], [u'blumberg', u'Blumberg', CAPS_NAMES], [u'roVsing', u'RoVsing', CAPS_NAMES], [u'Özdemir', u'Özdemir', CAPS_NAMES], [u'özdemir', u'Özdemir', CAPS_NAMES], ] for pair in pairs: result = capitalize(pair[0], pair[2]) if result != pair[1]: success = False print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1]) if success: print "... SUCCESS" return success #----------------------------------------------------------------------- def test_import_module(): print "testing import_module_from_directory()" path = sys.argv[1] name = sys.argv[2] try: mod = import_module_from_directory(module_path = path, module_name = name) except: print "module import failed, see log" return False print "module import succeeded", mod print dir(mod) return True #----------------------------------------------------------------------- def test_mkdir(): print "testing mkdir()" mkdir(sys.argv[1]) #----------------------------------------------------------------------- def test_gmPaths(): print "testing gmPaths()" print "-----------------" paths = gmPaths(wx=None, app_name='gnumed') print "user config dir:", paths.user_config_dir print "system config dir:", paths.system_config_dir print "local base dir:", paths.local_base_dir print "system app data dir:", paths.system_app_data_dir print "working directory :", paths.working_dir print "temp directory :", paths.tmp_dir #----------------------------------------------------------------------- def test_none_if(): print "testing none_if()" print "-----------------" tests = [ [None, None, None], ['a', 'a', None], ['a', 'b', 'a'], ['a', None, 'a'], [None, 'a', None], [1, 1, None], [1, 2, 1], [1, None, 1], [None, 1, None] ] for test in tests: if none_if(value = test[0], none_equivalent = test[1]) != test[2]: print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2]) return True #----------------------------------------------------------------------- def test_bool2str(): tests = [ [True, 'Yes', 'Yes', 'Yes'], [False, 'OK', 'not OK', 'not OK'] ] for test in tests: if bool2str(test[0], test[1], test[2]) != test[3]: print 'ERROR: bool2str(%s, %s, %s) returned [%s], expected [%s]' % (test[0], test[1], test[2], bool2str(test[0], test[1], test[2]), test[3]) return True #----------------------------------------------------------------------- def test_bool2subst(): print bool2subst(True, 'True', 'False', 'is None') print bool2subst(False, 'True', 'False', 'is None') print bool2subst(None, 'True', 'False', 'is None') #----------------------------------------------------------------------- def test_get_unique_filename(): print get_unique_filename() print get_unique_filename(prefix='test-') print get_unique_filename(suffix='tst') print get_unique_filename(prefix='test-', suffix='tst') print get_unique_filename(tmp_dir='/home/ncq/Archiv/') #----------------------------------------------------------------------- def test_size2str(): print "testing size2str()" print "------------------" tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000] for test in tests: print size2str(test) #----------------------------------------------------------------------- def test_unwrap(): test = """ second line\n 3rd starts with tab \n 4th with a space \n 6th """ print unwrap(text = test, max_length = 25) #----------------------------------------------------------------------- def test_wrap(): test = 'line 1\nline 2\nline 3' print "wrap 5-6-7 initial 0, subsequent 0" print wrap(test, 5) print print wrap(test, 6) print print wrap(test, 7) print "-------" raw_input() print "wrap 5 initial 1-1-3, subsequent 1-3-1" print wrap(test, 5, u' ', u' ') print print wrap(test, 5, u' ', u' ') print print wrap(test, 5, u' ', u' ') print "-------" raw_input() print "wrap 6 initial 1-1-3, subsequent 1-3-1" print wrap(test, 6, u' ', u' ') print print wrap(test, 6, u' ', u' ') print print wrap(test, 6, u' ', u' ') print "-------" raw_input() print "wrap 7 initial 1-1-3, subsequent 1-3-1" print wrap(test, 7, u' ', u' ') print print wrap(test, 7, u' ', u' ') print print wrap(test, 7, u' ', u' ') #----------------------------------------------------------------------- def test_md5(): print '%s: %s' % (sys.argv[2], file2md5(sys.argv[2])) #----------------------------------------------------------------------- def test_unicode(): print u_link_symbol * 10 #----------------------------------------------------------------------- #test_coalesce() #test_capitalize() #test_import_module() #test_mkdir() test_gmPaths() #test_none_if() #test_bool2str() #test_bool2subst() #test_get_unique_filename() #test_size2str() #test_wrap() #test_input2decimal() #test_input2int() #test_unwrap() #test_md5() #test_unicode() #===========================================================================