1
2 __doc__ = """GNUmed general tools."""
3
4
5 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
7
8
9 import re as regex, sys, os, os.path, csv, tempfile, logging, hashlib
10 import decimal
11 import cPickle, zlib
12 import xml.sax.saxutils as xml_tools
13
14
15
16 if __name__ == '__main__':
17
18 logging.basicConfig(level = logging.DEBUG)
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmI18N
21 gmI18N.activate_locale()
22 gmI18N.install_domain()
23
24 from Gnumed.pycommon import gmBorg
25
26
27 _log = logging.getLogger('gm.tools')
28
29
30 ( CAPS_NONE,
31 CAPS_FIRST,
32 CAPS_ALLCAPS,
33 CAPS_WORDS,
34 CAPS_NAMES,
35 CAPS_FIRST_ONLY
36 ) = range(6)
37
38
39 u_right_double_angle_quote = u'\u00AB'
40 u_registered_trademark = u'\u00AE'
41 u_plus_minus = u'\u00B1'
42 u_left_double_angle_quote = u'\u00BB'
43 u_one_quarter = u'\u00BC'
44 u_one_half = u'\u00BD'
45 u_three_quarters = u'\u00BE'
46 u_multiply = u'\u00D7'
47 u_ellipsis = u'\u2026'
48 u_euro = u'\u20AC'
49 u_numero = u'\u2116'
50 u_down_left_arrow = u'\u21B5'
51 u_left_arrow = u'\u2190'
52 u_right_arrow = u'\u2192'
53 u_sum = u'\u2211'
54 u_almost_equal_to = u'\u2248'
55 u_corresponds_to = u'\u2258'
56 u_infinity = u'\u221E'
57 u_diameter = u'\u2300'
58 u_checkmark_crossed_out = u'\u237B'
59 u_box_horiz_single = u'\u2500'
60 u_box_horiz_4dashes = u'\u2508'
61 u_box_top_double = u'\u2550'
62 u_box_top_left_double_single = u'\u2552'
63 u_box_top_right_double_single = u'\u2555'
64 u_box_top_left_arc = u'\u256d'
65 u_box_bottom_right_arc = u'\u256f'
66 u_box_bottom_left_arc = u'\u2570'
67 u_box_horiz_light_heavy = u'\u257c'
68 u_box_horiz_heavy_light = u'\u257e'
69 u_skull_and_crossbones = u'\u2620'
70 u_frowning_face = u'\u2639'
71 u_smiling_face = u'\u263a'
72 u_black_heart = u'\u2665'
73 u_checkmark_thin = u'\u2713'
74 u_checkmark_thick = u'\u2714'
75 u_writing_hand = u'\u270d'
76 u_pencil_1 = u'\u270e'
77 u_pencil_2 = u'\u270f'
78 u_pencil_3 = u'\u2710'
79 u_latin_cross = u'\u271d'
80 u_replacement_character = u'\ufffd'
81 u_link_symbol = u'\u1f517'
82
83
85
86 print ".========================================================"
87 print "| Unhandled exception caught !"
88 print "| Type :", t
89 print "| Value:", v
90 print "`========================================================"
91 _log.critical('unhandled exception caught', exc_info = (t,v,tb))
92 sys.__excepthook__(t,v,tb)
93
94
95
96 -def mkdir(directory=None):
97 try:
98 os.makedirs(directory)
99 except OSError, e:
100 if (e.errno == 17) and not os.path.isdir(directory):
101 raise
102 return True
103
104
106 """This class provides the following paths:
107
108 .home_dir
109 .local_base_dir
110 .working_dir
111 .user_config_dir
112 .system_config_dir
113 .system_app_data_dir
114 .tmp_dir (readonly)
115 """
116 - def __init__(self, app_name=None, wx=None):
117 """Setup pathes.
118
119 <app_name> will default to (name of the script - .py)
120 """
121 try:
122 self.already_inited
123 return
124 except AttributeError:
125 pass
126
127 self.init_paths(app_name=app_name, wx=wx)
128 self.already_inited = True
129
130
131
133
134 if wx is None:
135 _log.debug('wxPython not available')
136 _log.debug('detecting paths directly')
137
138 if app_name is None:
139 app_name, ext = os.path.splitext(os.path.basename(sys.argv[0]))
140 _log.info('app name detected as [%s]', app_name)
141 else:
142 _log.info('app name passed in as [%s]', app_name)
143
144
145 self.__home_dir = None
146
147
148
149 self.local_base_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
150
151
152 self.working_dir = os.path.abspath(os.curdir)
153
154
155
156
157 mkdir(os.path.join(self.home_dir, '.%s' % app_name))
158 self.user_config_dir = os.path.join(self.home_dir, '.%s' % app_name)
159
160
161 try:
162 self.system_config_dir = os.path.join('/etc', app_name)
163 except ValueError:
164
165 self.system_config_dir = self.user_config_dir
166
167
168 try:
169 self.system_app_data_dir = os.path.join(sys.prefix, 'share', app_name)
170 except ValueError:
171 self.system_app_data_dir = self.local_base_dir
172
173
174 try:
175 self.__tmp_dir_already_set
176 _log.debug('temp dir already set')
177 except AttributeError:
178 tmp_base = os.path.join(tempfile.gettempdir(), app_name)
179 mkdir(tmp_base)
180 _log.info('previous temp dir: %s', tempfile.gettempdir())
181 tempfile.tempdir = tmp_base
182 _log.info('intermediate temp dir: %s', tempfile.gettempdir())
183 self.tmp_dir = tempfile.mkdtemp(prefix = r'gm-')
184
185 self.__log_paths()
186 if wx is None:
187 return True
188
189
190 _log.debug('re-detecting paths with wxPython')
191
192 std_paths = wx.StandardPaths.Get()
193 _log.info('wxPython app name is [%s]', wx.GetApp().GetAppName())
194
195
196 mkdir(os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name))
197 self.user_config_dir = os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name)
198
199
200 try:
201 tmp = std_paths.GetConfigDir()
202 if not tmp.endswith(app_name):
203 tmp = os.path.join(tmp, app_name)
204 self.system_config_dir = tmp
205 except ValueError:
206
207 pass
208
209
210
211
212 if 'wxMSW' in wx.PlatformInfo:
213 _log.warning('this platform (wxMSW) sometimes returns a broken value for the system-wide application data dir')
214 else:
215 try:
216 self.system_app_data_dir = std_paths.GetDataDir()
217 except ValueError:
218 pass
219
220 self.__log_paths()
221 return True
222
224 _log.debug('sys.argv[0]: %s', sys.argv[0])
225 _log.debug('local application base dir: %s', self.local_base_dir)
226 _log.debug('current working dir: %s', self.working_dir)
227
228 _log.debug('user home dir: %s', self.home_dir)
229 _log.debug('user-specific config dir: %s', self.user_config_dir)
230 _log.debug('system-wide config dir: %s', self.system_config_dir)
231 _log.debug('system-wide application data dir: %s', self.system_app_data_dir)
232 _log.debug('temporary dir: %s', self.tmp_dir)
233
234
235
237 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
238 msg = '[%s:user_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
239 _log.error(msg)
240 raise ValueError(msg)
241 self.__user_config_dir = path
242
244 return self.__user_config_dir
245
246 user_config_dir = property(_get_user_config_dir, _set_user_config_dir)
247
249 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
250 msg = '[%s:system_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
251 _log.error(msg)
252 raise ValueError(msg)
253 self.__system_config_dir = path
254
256 return self.__system_config_dir
257
258 system_config_dir = property(_get_system_config_dir, _set_system_config_dir)
259
261 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
262 msg = '[%s:system_app_data_dir]: invalid path [%s]' % (self.__class__.__name__, path)
263 _log.error(msg)
264 raise ValueError(msg)
265 self.__system_app_data_dir = path
266
268 return self.__system_app_data_dir
269
270 system_app_data_dir = property(_get_system_app_data_dir, _set_system_app_data_dir)
271
273 raise ValueError('invalid to set home dir')
274
276 if self.__home_dir is not None:
277 return self.__home_dir
278
279 tmp = os.path.expanduser('~')
280 if tmp == '~':
281 _log.error('this platform does not expand ~ properly')
282 try:
283 tmp = os.environ['USERPROFILE']
284 except KeyError:
285 _log.error('cannot access $USERPROFILE in environment')
286
287 if not (
288 os.access(tmp, os.R_OK)
289 and
290 os.access(tmp, os.X_OK)
291 and
292 os.access(tmp, os.W_OK)
293 ):
294 msg = '[%s:home_dir]: invalid path [%s]' % (self.__class__.__name__, tmp)
295 _log.error(msg)
296 raise ValueError(msg)
297
298 self.__home_dir = tmp
299 return self.__home_dir
300
301 home_dir = property(_get_home_dir, _set_home_dir)
302
304 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
305 msg = '[%s:tmp_dir]: invalid path [%s]' % (self.__class__.__name__, path)
306 _log.error(msg)
307 raise ValueError(msg)
308 _log.debug('previous temp dir: %s', tempfile.gettempdir())
309 self.__tmp_dir = path
310 tempfile.tempdir = self.__tmp_dir
311 self.__tmp_dir_already_set = True
312
314 return self.__tmp_dir
315
316 tmp_dir = property(_get_tmp_dir, _set_tmp_dir)
317
318
319
320 -def file2md5(filename=None, return_hex=True):
321 blocksize = 2**10 * 128
322 _log.debug('md5(%s): <%s> byte blocks', filename, blocksize)
323
324 f = open(filename, 'rb')
325
326 md5 = hashlib.md5()
327 while True:
328 data = f.read(blocksize)
329 if not data:
330 break
331 md5.update(data)
332
333 _log.debug('md5(%s): %s', filename, md5.hexdigest())
334
335 if return_hex:
336 return md5.hexdigest()
337 return md5.digest()
338
340 for line in unicode_csv_data:
341 yield line.encode(encoding)
342
343
344
345
346
347 default_csv_reader_rest_key = u'list_of_values_of_unknown_fields'
348
350
351
352 try:
353 is_dict_reader = kwargs['dict']
354 del kwargs['dict']
355 if is_dict_reader is not True:
356 raise KeyError
357 kwargs['restkey'] = default_csv_reader_rest_key
358 csv_reader = csv.DictReader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
359 except KeyError:
360 is_dict_reader = False
361 csv_reader = csv.reader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
362
363 for row in csv_reader:
364
365 if is_dict_reader:
366 for key in row.keys():
367 if key == default_csv_reader_rest_key:
368 old_data = row[key]
369 new_data = []
370 for val in old_data:
371 new_data.append(unicode(val, encoding))
372 row[key] = new_data
373 if default_csv_reader_rest_key not in csv_reader.fieldnames:
374 csv_reader.fieldnames.append(default_csv_reader_rest_key)
375 else:
376 row[key] = unicode(row[key], encoding)
377 yield row
378 else:
379 yield [ unicode(cell, encoding) for cell in row ]
380
381
383 """This introduces a race condition between the file.close() and
384 actually using the filename.
385
386 The file will not exist after calling this function.
387 """
388 if tmp_dir is not None:
389 if (
390 not os.access(tmp_dir, os.F_OK)
391 or
392 not os.access(tmp_dir, os.X_OK | os.W_OK)
393 ):
394 _log.info('cannot find temporary dir [%s], using system default', tmp_dir)
395 tmp_dir = None
396
397 kwargs = {'dir': tmp_dir}
398
399 if prefix is None:
400 kwargs['prefix'] = 'gnumed-'
401 else:
402 kwargs['prefix'] = prefix
403
404 if suffix in [None, u'']:
405 kwargs['suffix'] = '.tmp'
406 else:
407 if not suffix.startswith('.'):
408 suffix = '.' + suffix
409 kwargs['suffix'] = suffix
410
411 f = tempfile.NamedTemporaryFile(**kwargs)
412 filename = f.name
413 f.close()
414
415 return filename
416
418 """Import a module from any location."""
419
420 remove_path = always_remove_path or False
421 if module_path not in sys.path:
422 _log.info('appending to sys.path: [%s]' % module_path)
423 sys.path.append(module_path)
424 remove_path = True
425
426 _log.debug('will remove import path: %s', remove_path)
427
428 if module_name.endswith('.py'):
429 module_name = module_name[:-3]
430
431 try:
432 module = __import__(module_name)
433 except StandardError:
434 _log.exception('cannot __import__() module [%s] from [%s]' % (module_name, module_path))
435 while module_path in sys.path:
436 sys.path.remove(module_path)
437 raise
438
439 _log.info('imported module [%s] as [%s]' % (module_name, module))
440 if remove_path:
441 while module_path in sys.path:
442 sys.path.remove(module_path)
443
444 return module
445
446
447
448 _kB = 1024
449 _MB = 1024 * _kB
450 _GB = 1024 * _MB
451 _TB = 1024 * _GB
452 _PB = 1024 * _TB
453
455 if size == 1:
456 return template % _('1 Byte')
457 if size < 10 * _kB:
458 return template % _('%s Bytes') % size
459 if size < _MB:
460 return template % u'%.1f kB' % (float(size) / _kB)
461 if size < _GB:
462 return template % u'%.1f MB' % (float(size) / _MB)
463 if size < _TB:
464 return template % u'%.1f GB' % (float(size) / _GB)
465 if size < _PB:
466 return template % u'%.1f TB' % (float(size) / _TB)
467 return template % u'%.1f PB' % (float(size) / _PB)
468
469 -def bool2subst(boolean=None, true_return=True, false_return=False, none_return=None):
470 if boolean is None:
471 return none_return
472 if boolean is True:
473 return true_return
474 if boolean is False:
475 return false_return
476 raise ValueError('bool2subst(): <boolean> arg must be either of True, False, None')
477
478 -def bool2str(boolean=None, true_str='True', false_str='False'):
479 return bool2subst (
480 boolean = bool(boolean),
481 true_return = true_str,
482 false_return = false_str
483 )
484
485 -def none_if(value=None, none_equivalent=None, strip_string=False):
486 """Modelled after the SQL NULLIF function."""
487 if value is None:
488 return None
489 if strip_string:
490 stripped = value.strip()
491 else:
492 stripped = value
493 if stripped == none_equivalent:
494 return None
495 return value
496
497 -def coalesce(initial=None, instead=None, template_initial=None, template_instead=None, none_equivalents=None, function_initial=None):
498 """Modelled after the SQL coalesce function.
499
500 To be used to simplify constructs like:
501
502 if initial is None (or in none_equivalents):
503 real_value = (template_instead % instead) or instead
504 else:
505 real_value = (template_initial % initial) or initial
506 print real_value
507
508 @param initial: the value to be tested for <None>
509 @type initial: any Python type, must have a __str__ method if template_initial is not None
510 @param instead: the value to be returned if <initial> is None
511 @type instead: any Python type, must have a __str__ method if template_instead is not None
512 @param template_initial: if <initial> is returned replace the value into this template, must contain one <%s>
513 @type template_initial: string or None
514 @param template_instead: if <instead> is returned replace the value into this template, must contain one <%s>
515 @type template_instead: string or None
516
517 example:
518 function_initial = ('strftime', '%Y-%m-%d')
519
520 Ideas:
521 - list of insteads: initial, [instead, template], [instead, template], [instead, template], template_initial, ...
522 """
523 if none_equivalents is None:
524 none_equivalents = [None]
525
526 if initial in none_equivalents:
527
528 if template_instead is None:
529 return instead
530
531 return template_instead % instead
532
533 if function_initial is not None:
534 funcname, args = function_initial
535 func = getattr(initial, funcname)
536 initial = func(args)
537
538 if template_initial is None:
539 return initial
540
541 try:
542 return template_initial % initial
543 except TypeError:
544 return template_initial
545
547 val = match_obj.group(0).lower()
548 if val in ['von', 'van', 'de', 'la', 'l', 'der', 'den']:
549 return val
550 buf = list(val)
551 buf[0] = buf[0].upper()
552 for part in ['mac', 'mc', 'de', 'la']:
553 if len(val) > len(part) and val[:len(part)] == part:
554 buf[len(part)] = buf[len(part)].upper()
555 return ''.join(buf)
556
558 """Capitalize the first character but leave the rest alone.
559
560 Note that we must be careful about the locale, this may
561 have issues ! However, for UTF strings it should just work.
562 """
563 if (mode is None) or (mode == CAPS_NONE):
564 return text
565
566 if mode == CAPS_FIRST:
567 if len(text) == 1:
568 return text[0].upper()
569 return text[0].upper() + text[1:]
570
571 if mode == CAPS_ALLCAPS:
572 return text.upper()
573
574 if mode == CAPS_FIRST_ONLY:
575 if len(text) == 1:
576 return text[0].upper()
577 return text[0].upper() + text[1:].lower()
578
579 if mode == CAPS_WORDS:
580 return regex.sub(ur'(\w)(\w+)', lambda x: x.group(1).upper() + x.group(2).lower(), text)
581
582 if mode == CAPS_NAMES:
583
584 return capitalize(text=text, mode=CAPS_FIRST)
585
586 print "ERROR: invalid capitalization mode: [%s], leaving input as is" % mode
587 return text
588
610
636
638 return_join = False
639 if lines is None:
640 return_join = True
641 lines = eol.split(text)
642
643 while True:
644 if lines[0].strip(eol).strip() != u'':
645 break
646 lines = lines[1:]
647
648 if return_join:
649 return eol.join(lines)
650
651 return lines
652
654 return_join = False
655 if lines is None:
656 return_join = True
657 lines = eol.split(text)
658
659 while True:
660 if lines[-1].strip(eol).strip() != u'':
661 break
662 lines = lines[:-1]
663
664 if return_join:
665 return eol.join(lines)
666
667 return lines
668
669 -def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'):
670 """A word-wrap function that preserves existing line breaks
671 and most spaces in the text. Expects that existing line
672 breaks are posix newlines (\n).
673 """
674 wrapped = initial_indent + reduce (
675 lambda line, word, width=width: '%s%s%s' % (
676 line,
677 ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)],
678 word
679 ),
680 text.split(' ')
681 )
682
683 if subsequent_indent != u'':
684 wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n'))
685
686 if eol != u'\n':
687 wrapped = wrapped.replace('\n', eol)
688
689 return wrapped
690
691 -def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '):
692
693 text = text.replace(u'\r', u'')
694 lines = text.split(u'\n')
695 text = u''
696 for line in lines:
697
698 if strip_whitespace:
699 line = line.strip().strip(u'\t').strip()
700
701 if remove_empty_lines:
702 if line == u'':
703 continue
704
705 text += (u'%s%s' % (line, line_separator))
706
707 text = text.rstrip(line_separator)
708
709 if max_length is not None:
710 text = text[:max_length]
711
712 text = text.rstrip(line_separator)
713
714 return text
715
717 """check for special XML characters and transform them"""
718 return xml_tools.escape (
719 text,
720 entities = {
721 u'&': u'&'
722 }
723 )
724
725
726
728 """check for special LaTeX characters and transform them"""
729
730 text = text.replace(u'\\', u'$\\backslash$')
731 text = text.replace(u'{', u'\\{')
732 text = text.replace(u'}', u'\\}')
733 text = text.replace(u'%', u'\\%')
734 text = text.replace(u'&', u'\\&')
735 text = text.replace(u'#', u'\\#')
736 text = text.replace(u'$', u'\\$')
737 text = text.replace(u'_', u'\\_')
738 text = text.replace(u_euro, u'\\EUR')
739
740 text = text.replace(u'^', u'\\verb#^#')
741 text = text.replace('~','\\verb#~#')
742
743 return text
744
771
772
773
774
775
776 __icon_serpent = \
777 """x\xdae\x8f\xb1\x0e\x83 \x10\x86w\x9f\xe2\x92\x1blb\xf2\x07\x96\xeaH:0\xd6\
778 \xc1\x85\xd5\x98N5\xa5\xef?\xf5N\xd0\x8a\xdcA\xc2\xf7qw\x84\xdb\xfa\xb5\xcd\
779 \xd4\xda;\xc9\x1a\xc8\xb6\xcd<\xb5\xa0\x85\x1e\xeb\xbc\xbc7b!\xf6\xdeHl\x1c\
780 \x94\x073\xec<*\xf7\xbe\xf7\x99\x9d\xb21~\xe7.\xf5\x1f\x1c\xd3\xbdVlL\xc2\
781 \xcf\xf8ye\xd0\x00\x90\x0etH \x84\x80B\xaa\x8a\x88\x85\xc4(U\x9d$\xfeR;\xc5J\
782 \xa6\x01\xbbt9\xceR\xc8\x81e_$\x98\xb9\x9c\xa9\x8d,y\xa9t\xc8\xcf\x152\xe0x\
783 \xe9$\xf5\x07\x95\x0cD\x95t:\xb1\x92\xae\x9cI\xa8~\x84\x1f\xe0\xa3ec"""
784
786
787 paths = gmPaths(app_name = u'gnumed', wx = wx)
788
789 candidates = [
790 os.path.join(paths.system_app_data_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
791 os.path.join(paths.local_base_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
792 os.path.join(paths.system_app_data_dir, 'bitmaps', 'serpent.png'),
793 os.path.join(paths.local_base_dir, 'bitmaps', 'serpent.png')
794 ]
795
796 found_as = None
797 for candidate in candidates:
798 try:
799 open(candidate, 'r').close()
800 found_as = candidate
801 break
802 except IOError:
803 _log.debug('icon not found in [%s]', candidate)
804
805 if found_as is None:
806 _log.warning('no icon file found, falling back to builtin (ugly) icon')
807 icon_bmp_data = wx.BitmapFromXPMData(cPickle.loads(zlib.decompress(__icon_serpent)))
808 icon.CopyFromBitmap(icon_bmp_data)
809 else:
810 _log.debug('icon found in [%s]', found_as)
811 icon = wx.EmptyIcon()
812 try:
813 icon.LoadFile(found_as, wx.BITMAP_TYPE_ANY)
814 except AttributeError:
815 _log.exception(u"this platform doesn't support wx.Icon().LoadFile()")
816
817 return icon
818
819
820
821 if __name__ == '__main__':
822
823 if len(sys.argv) < 2:
824 sys.exit()
825
826 if sys.argv[1] != 'test':
827 sys.exit()
828
829
887
892
894
895 import datetime as dt
896 print coalesce(initial = dt.datetime.now(), template_initial = u'-- %s --', function_initial = ('strftime', u'%Y-%m-%d'))
897
898 print 'testing coalesce()'
899 print "------------------"
900 tests = [
901 [None, 'something other than <None>', None, None, 'something other than <None>'],
902 ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'],
903 ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'],
904 ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'],
905 [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'],
906 [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None']
907 ]
908 passed = True
909 for test in tests:
910 result = coalesce (
911 initial = test[0],
912 instead = test[1],
913 template_initial = test[2],
914 template_instead = test[3]
915 )
916 if result != test[4]:
917 print "ERROR"
918 print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3])
919 print "expected:", test[4]
920 print "received:", result
921 passed = False
922
923 if passed:
924 print "passed"
925 else:
926 print "failed"
927 return passed
928
930 print 'testing capitalize() ...'
931 success = True
932 pairs = [
933
934 [u'Boot', u'Boot', CAPS_FIRST_ONLY],
935 [u'boot', u'Boot', CAPS_FIRST_ONLY],
936 [u'booT', u'Boot', CAPS_FIRST_ONLY],
937 [u'BoOt', u'Boot', CAPS_FIRST_ONLY],
938 [u'boots-Schau', u'Boots-Schau', CAPS_WORDS],
939 [u'boots-sChau', u'Boots-Schau', CAPS_WORDS],
940 [u'boot camp', u'Boot Camp', CAPS_WORDS],
941 [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES],
942 [u'häkkönen', u'Häkkönen', CAPS_NAMES],
943 [u'McBurney', u'McBurney', CAPS_NAMES],
944 [u'mcBurney', u'McBurney', CAPS_NAMES],
945 [u'blumberg', u'Blumberg', CAPS_NAMES],
946 [u'roVsing', u'RoVsing', CAPS_NAMES],
947 [u'Özdemir', u'Özdemir', CAPS_NAMES],
948 [u'özdemir', u'Özdemir', CAPS_NAMES],
949 ]
950 for pair in pairs:
951 result = capitalize(pair[0], pair[2])
952 if result != pair[1]:
953 success = False
954 print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1])
955
956 if success:
957 print "... SUCCESS"
958
959 return success
960
962 print "testing import_module_from_directory()"
963 path = sys.argv[1]
964 name = sys.argv[2]
965 try:
966 mod = import_module_from_directory(module_path = path, module_name = name)
967 except:
968 print "module import failed, see log"
969 return False
970
971 print "module import succeeded", mod
972 print dir(mod)
973 return True
974
976 print "testing mkdir()"
977 mkdir(sys.argv[1])
978
980 print "testing gmPaths()"
981 print "-----------------"
982 paths = gmPaths(wx=None, app_name='gnumed')
983 print "user config dir:", paths.user_config_dir
984 print "system config dir:", paths.system_config_dir
985 print "local base dir:", paths.local_base_dir
986 print "system app data dir:", paths.system_app_data_dir
987 print "working directory :", paths.working_dir
988 print "temp directory :", paths.tmp_dir
989
991 print "testing none_if()"
992 print "-----------------"
993 tests = [
994 [None, None, None],
995 ['a', 'a', None],
996 ['a', 'b', 'a'],
997 ['a', None, 'a'],
998 [None, 'a', None],
999 [1, 1, None],
1000 [1, 2, 1],
1001 [1, None, 1],
1002 [None, 1, None]
1003 ]
1004
1005 for test in tests:
1006 if none_if(value = test[0], none_equivalent = test[1]) != test[2]:
1007 print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2])
1008
1009 return True
1010
1012 tests = [
1013 [True, 'Yes', 'Yes', 'Yes'],
1014 [False, 'OK', 'not OK', 'not OK']
1015 ]
1016 for test in tests:
1017 if bool2str(test[0], test[1], test[2]) != test[3]:
1018 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])
1019
1020 return True
1021
1023
1024 print bool2subst(True, 'True', 'False', 'is None')
1025 print bool2subst(False, 'True', 'False', 'is None')
1026 print bool2subst(None, 'True', 'False', 'is None')
1027
1034
1036 print "testing size2str()"
1037 print "------------------"
1038 tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000]
1039 for test in tests:
1040 print size2str(test)
1041
1043
1044 test = """
1045 second line\n
1046 3rd starts with tab \n
1047 4th with a space \n
1048
1049 6th
1050
1051 """
1052 print unwrap(text = test, max_length = 25)
1053
1055 test = 'line 1\nline 2\nline 3'
1056
1057 print "wrap 5-6-7 initial 0, subsequent 0"
1058 print wrap(test, 5)
1059 print
1060 print wrap(test, 6)
1061 print
1062 print wrap(test, 7)
1063 print "-------"
1064 raw_input()
1065 print "wrap 5 initial 1-1-3, subsequent 1-3-1"
1066 print wrap(test, 5, u' ', u' ')
1067 print
1068 print wrap(test, 5, u' ', u' ')
1069 print
1070 print wrap(test, 5, u' ', u' ')
1071 print "-------"
1072 raw_input()
1073 print "wrap 6 initial 1-1-3, subsequent 1-3-1"
1074 print wrap(test, 6, u' ', u' ')
1075 print
1076 print wrap(test, 6, u' ', u' ')
1077 print
1078 print wrap(test, 6, u' ', u' ')
1079 print "-------"
1080 raw_input()
1081 print "wrap 7 initial 1-1-3, subsequent 1-3-1"
1082 print wrap(test, 7, u' ', u' ')
1083 print
1084 print wrap(test, 7, u' ', u' ')
1085 print
1086 print wrap(test, 7, u' ', u' ')
1087
1089 print '%s: %s' % (sys.argv[2], file2md5(sys.argv[2]))
1090
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109 test_unicode()
1110
1111
1112