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