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