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 platform
11 import subprocess
12 import decimal
13 import cPickle, zlib
14 import xml.sax.saxutils as xml_tools
15
16
17
18 if __name__ == '__main__':
19
20 logging.basicConfig(level = logging.DEBUG)
21 sys.path.insert(0, '../../')
22 from Gnumed.pycommon import gmI18N
23 gmI18N.activate_locale()
24 gmI18N.install_domain()
25
26 from Gnumed.pycommon import gmBorg
27
28
29 _log = logging.getLogger('gm.tools')
30
31
32 ( CAPS_NONE,
33 CAPS_FIRST,
34 CAPS_ALLCAPS,
35 CAPS_WORDS,
36 CAPS_NAMES,
37 CAPS_FIRST_ONLY
38 ) = range(6)
39
40
41 u_currency_pound = u'\u00A3'
42 u_currency_sign = u'\u00A4'
43 u_currency_yen = u'\u00A5'
44 u_right_double_angle_quote = u'\u00AB'
45 u_registered_trademark = u'\u00AE'
46 u_plus_minus = u'\u00B1'
47 u_left_double_angle_quote = u'\u00BB'
48 u_one_quarter = u'\u00BC'
49 u_one_half = u'\u00BD'
50 u_three_quarters = u'\u00BE'
51 u_multiply = u'\u00D7'
52 u_greek_ALPHA = u'\u0391'
53 u_greek_alpha = u'\u03b1'
54 u_greek_OMEGA = u'\u03A9'
55 u_greek_omega = u'\u03c9'
56 u_triangular_bullet = u'\u2023'
57 u_ellipsis = u'\u2026'
58 u_euro = u'\u20AC'
59 u_numero = u'\u2116'
60 u_down_left_arrow = u'\u21B5'
61 u_left_arrow = u'\u2190'
62 u_right_arrow = u'\u2192'
63 u_left_arrow_with_tail = u'\u21a2'
64 u_sum = u'\u2211'
65 u_almost_equal_to = u'\u2248'
66 u_corresponds_to = u'\u2258'
67 u_infinity = u'\u221E'
68 u_diameter = u'\u2300'
69 u_checkmark_crossed_out = u'\u237B'
70 u_box_horiz_single = u'\u2500'
71 u_box_horiz_4dashes = u'\u2508'
72 u_box_top_double = u'\u2550'
73 u_box_top_left_double_single = u'\u2552'
74 u_box_top_right_double_single = u'\u2555'
75 u_box_top_left_arc = u'\u256d'
76 u_box_bottom_right_arc = u'\u256f'
77 u_box_bottom_left_arc = u'\u2570'
78 u_box_horiz_light_heavy = u'\u257c'
79 u_box_horiz_heavy_light = u'\u257e'
80 u_skull_and_crossbones = u'\u2620'
81 u_frowning_face = u'\u2639'
82 u_smiling_face = u'\u263a'
83 u_black_heart = u'\u2665'
84 u_checkmark_thin = u'\u2713'
85 u_checkmark_thick = u'\u2714'
86 u_writing_hand = u'\u270d'
87 u_pencil_1 = u'\u270e'
88 u_pencil_2 = u'\u270f'
89 u_pencil_3 = u'\u2710'
90 u_latin_cross = u'\u271d'
91 u_kanji_yen = u'\u5186'
92 u_replacement_character = u'\ufffd'
93 u_link_symbol = u'\u1f517'
94
95
97
98 print ".========================================================"
99 print "| Unhandled exception caught !"
100 print "| Type :", t
101 print "| Value:", v
102 print "`========================================================"
103 _log.critical('unhandled exception caught', exc_info = (t,v,tb))
104 sys.__excepthook__(t,v,tb)
105
106
107
108 -def mkdir(directory=None):
109 try:
110 os.makedirs(directory)
111 except OSError, e:
112 if (e.errno == 17) and not os.path.isdir(directory):
113 raise
114 return True
115
116
118 """This class provides the following paths:
119
120 .home_dir
121 .local_base_dir
122 .working_dir
123 .user_config_dir
124 .system_config_dir
125 .system_app_data_dir
126 .tmp_dir (readonly)
127 """
128 - def __init__(self, app_name=None, wx=None):
129 """Setup pathes.
130
131 <app_name> will default to (name of the script - .py)
132 """
133 try:
134 self.already_inited
135 return
136 except AttributeError:
137 pass
138
139 self.init_paths(app_name=app_name, wx=wx)
140 self.already_inited = True
141
142
143
145
146 if wx is None:
147 _log.debug('wxPython not available')
148 _log.debug('detecting paths directly')
149
150 if app_name is None:
151 app_name, ext = os.path.splitext(os.path.basename(sys.argv[0]))
152 _log.info('app name detected as [%s]', app_name)
153 else:
154 _log.info('app name passed in as [%s]', app_name)
155
156
157 self.__home_dir = None
158
159
160 if getattr(sys, 'frozen', False):
161 _log.info('frozen app, installed into temporary path')
162
163
164
165
166
167
168
169
170
171 self.local_base_dir = os.path.dirname(sys.executable)
172 else:
173 self.local_base_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
174
175
176 self.working_dir = os.path.abspath(os.curdir)
177
178
179 mkdir(os.path.join(self.home_dir, '.%s' % app_name))
180 self.user_config_dir = os.path.join(self.home_dir, '.%s' % app_name)
181
182
183 try:
184 self.system_config_dir = os.path.join('/etc', app_name)
185 except ValueError:
186
187 self.system_config_dir = self.user_config_dir
188
189
190 try:
191 self.system_app_data_dir = os.path.join(sys.prefix, 'share', app_name)
192 except ValueError:
193 self.system_app_data_dir = self.local_base_dir
194
195
196 try:
197 self.__tmp_dir_already_set
198 _log.debug('temp dir already set')
199 except AttributeError:
200 tmp_base = os.path.join(tempfile.gettempdir(), app_name)
201 mkdir(tmp_base)
202 _log.info('previous temp dir: %s', tempfile.gettempdir())
203 tempfile.tempdir = tmp_base
204 _log.info('intermediate temp dir: %s', tempfile.gettempdir())
205 self.tmp_dir = tempfile.mkdtemp(prefix = r'gm-')
206
207 self.__log_paths()
208 if wx is None:
209 return True
210
211
212 _log.debug('re-detecting paths with wxPython')
213
214 std_paths = wx.StandardPaths.Get()
215 _log.info('wxPython app name is [%s]', wx.GetApp().GetAppName())
216
217
218 mkdir(os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name))
219 self.user_config_dir = os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name)
220
221
222 try:
223 tmp = std_paths.GetConfigDir()
224 if not tmp.endswith(app_name):
225 tmp = os.path.join(tmp, app_name)
226 self.system_config_dir = tmp
227 except ValueError:
228
229 pass
230
231
232
233
234 if 'wxMSW' in wx.PlatformInfo:
235 _log.warning('this platform (wxMSW) sometimes returns a broken value for the system-wide application data dir')
236 else:
237 try:
238 self.system_app_data_dir = std_paths.GetDataDir()
239 except ValueError:
240 pass
241
242 self.__log_paths()
243 return True
244
246 _log.debug('sys.argv[0]: %s', sys.argv[0])
247 _log.debug('sys.executable: %s', sys.executable)
248 _log.debug('sys._MEIPASS: %s', getattr(sys, '_MEIPASS', '<not found>'))
249 _log.debug('os.environ["_MEIPASS2"]: %s', os.environ.get('_MEIPASS2', '<not found>'))
250 _log.debug('__file__ : %s', __file__)
251 _log.debug('local application base dir: %s', self.local_base_dir)
252 _log.debug('current working dir: %s', self.working_dir)
253 _log.debug('user home dir: %s', self.home_dir)
254 _log.debug('user-specific config dir: %s', self.user_config_dir)
255 _log.debug('system-wide config dir: %s', self.system_config_dir)
256 _log.debug('system-wide application data dir: %s', self.system_app_data_dir)
257 _log.debug('temporary dir: %s', self.tmp_dir)
258
259
260
262 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
263 msg = '[%s:user_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
264 _log.error(msg)
265 raise ValueError(msg)
266 self.__user_config_dir = path
267
269 return self.__user_config_dir
270
271 user_config_dir = property(_get_user_config_dir, _set_user_config_dir)
272
274 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
275 msg = '[%s:system_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
276 _log.error(msg)
277 raise ValueError(msg)
278 self.__system_config_dir = path
279
281 return self.__system_config_dir
282
283 system_config_dir = property(_get_system_config_dir, _set_system_config_dir)
284
286 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
287 msg = '[%s:system_app_data_dir]: invalid path [%s]' % (self.__class__.__name__, path)
288 _log.error(msg)
289 raise ValueError(msg)
290 self.__system_app_data_dir = path
291
293 return self.__system_app_data_dir
294
295 system_app_data_dir = property(_get_system_app_data_dir, _set_system_app_data_dir)
296
298 raise ValueError('invalid to set home dir')
299
301 if self.__home_dir is not None:
302 return self.__home_dir
303
304 tmp = os.path.expanduser('~')
305 if tmp == '~':
306 _log.error('this platform does not expand ~ properly')
307 try:
308 tmp = os.environ['USERPROFILE']
309 except KeyError:
310 _log.error('cannot access $USERPROFILE in environment')
311
312 if not (
313 os.access(tmp, os.R_OK)
314 and
315 os.access(tmp, os.X_OK)
316 and
317 os.access(tmp, os.W_OK)
318 ):
319 msg = '[%s:home_dir]: invalid path [%s]' % (self.__class__.__name__, tmp)
320 _log.error(msg)
321 raise ValueError(msg)
322
323 self.__home_dir = tmp
324 return self.__home_dir
325
326 home_dir = property(_get_home_dir, _set_home_dir)
327
329 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
330 msg = '[%s:tmp_dir]: invalid path [%s]' % (self.__class__.__name__, path)
331 _log.error(msg)
332 raise ValueError(msg)
333 _log.debug('previous temp dir: %s', tempfile.gettempdir())
334 self.__tmp_dir = path
335 tempfile.tempdir = self.__tmp_dir
336 self.__tmp_dir_already_set = True
337
339 return self.__tmp_dir
340
341 tmp_dir = property(_get_tmp_dir, _set_tmp_dir)
342
343
344
346
347 if platform.system() == 'Windows':
348 exec_name = 'gpg.exe'
349 else:
350 exec_name = 'gpg'
351
352 tmp, fname = os.path.split(filename)
353 basename, tmp = os.path.splitext(fname)
354 filename_decrypted = get_unique_filename(prefix = '%s-decrypted-' % basename)
355
356 args = [exec_name, '--verbose', '--batch', '--yes', '--passphrase-fd', '0', '--output', filename_decrypted, '--decrypt', filename]
357 _log.debug('GnuPG args: %s' % str(args))
358
359 try:
360 gpg = subprocess.Popen (
361 args = args,
362 stdin = subprocess.PIPE,
363 stdout = subprocess.PIPE,
364 stderr = subprocess.PIPE,
365 close_fds = False
366 )
367 except (OSError, ValueError, subprocess.CalledProcessError):
368 _log.exception('there was a problem executing gpg')
369 gmDispatcher.send(signal = u'statustext', msg = _('Error running GnuPG. Cannot decrypt data.'), beep = True)
370 return
371
372 out, error = gpg.communicate(passphrase)
373 _log.debug('gpg returned [%s]', gpg.returncode)
374 if gpg.returncode != 0:
375 _log.debug('GnuPG STDOUT:\n%s', out)
376 _log.debug('GnuPG STDERR:\n%s', error)
377 return None
378
379 return filename_decrypted
380
381 -def file2md5(filename=None, return_hex=True):
382 blocksize = 2**10 * 128
383 _log.debug('md5(%s): <%s> byte blocks', filename, blocksize)
384
385 f = open(filename, 'rb')
386
387 md5 = hashlib.md5()
388 while True:
389 data = f.read(blocksize)
390 if not data:
391 break
392 md5.update(data)
393
394 _log.debug('md5(%s): %s', filename, md5.hexdigest())
395
396 if return_hex:
397 return md5.hexdigest()
398 return md5.digest()
399
401 for line in unicode_csv_data:
402 yield line.encode(encoding)
403
404
405
406
407
408 default_csv_reader_rest_key = u'list_of_values_of_unknown_fields'
409
411
412
413 try:
414 is_dict_reader = kwargs['dict']
415 del kwargs['dict']
416 if is_dict_reader is not True:
417 raise KeyError
418 kwargs['restkey'] = default_csv_reader_rest_key
419 csv_reader = csv.DictReader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
420 except KeyError:
421 is_dict_reader = False
422 csv_reader = csv.reader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
423
424 for row in csv_reader:
425
426 if is_dict_reader:
427 for key in row.keys():
428 if key == default_csv_reader_rest_key:
429 old_data = row[key]
430 new_data = []
431 for val in old_data:
432 new_data.append(unicode(val, encoding))
433 row[key] = new_data
434 if default_csv_reader_rest_key not in csv_reader.fieldnames:
435 csv_reader.fieldnames.append(default_csv_reader_rest_key)
436 else:
437 row[key] = unicode(row[key], encoding)
438 yield row
439 else:
440 yield [ unicode(cell, encoding) for cell in row ]
441
442
443
445 return os.path.splitext(os.path.basename(filename))[0]
446
447
449 """This introduces a race condition between the file.close() and
450 actually using the filename.
451
452 The file will NOT exist after calling this function.
453 """
454 if tmp_dir is not None:
455 if (
456 not os.access(tmp_dir, os.F_OK)
457 or
458 not os.access(tmp_dir, os.X_OK | os.W_OK)
459 ):
460 _log.warning('cannot find temporary dir [%s], using system default', tmp_dir)
461 tmp_dir = None
462
463 kwargs = {'dir': tmp_dir}
464
465 if prefix is None:
466 kwargs['prefix'] = 'gnumed-'
467 else:
468 kwargs['prefix'] = prefix
469
470 if suffix in [None, u'']:
471 kwargs['suffix'] = '.tmp'
472 else:
473 if not suffix.startswith('.'):
474 suffix = '.' + suffix
475 kwargs['suffix'] = suffix
476
477 f = tempfile.NamedTemporaryFile(**kwargs)
478 filename = f.name
479 f.close()
480
481 return filename
482
484 """Import a module from any location."""
485
486 _log.debug('CWD: %s', os.getcwd())
487
488 remove_path = always_remove_path or False
489 if module_path not in sys.path:
490 _log.info('appending to sys.path: [%s]' % module_path)
491 sys.path.append(module_path)
492 remove_path = True
493
494 _log.debug('will remove import path: %s', remove_path)
495
496 if module_name.endswith('.py'):
497 module_name = module_name[:-3]
498
499 try:
500 module = __import__(module_name)
501 except StandardError:
502 _log.exception('cannot __import__() module [%s] from [%s]' % (module_name, module_path))
503 while module_path in sys.path:
504 sys.path.remove(module_path)
505 raise
506
507 _log.info('imported module [%s] as [%s]' % (module_name, module))
508 if remove_path:
509 while module_path in sys.path:
510 sys.path.remove(module_path)
511
512 return module
513
514
515
516 _kB = 1024
517 _MB = 1024 * _kB
518 _GB = 1024 * _MB
519 _TB = 1024 * _GB
520 _PB = 1024 * _TB
521
523 if size == 1:
524 return template % _('1 Byte')
525 if size < 10 * _kB:
526 return template % _('%s Bytes') % size
527 if size < _MB:
528 return template % u'%.1f kB' % (float(size) / _kB)
529 if size < _GB:
530 return template % u'%.1f MB' % (float(size) / _MB)
531 if size < _TB:
532 return template % u'%.1f GB' % (float(size) / _GB)
533 if size < _PB:
534 return template % u'%.1f TB' % (float(size) / _TB)
535 return template % u'%.1f PB' % (float(size) / _PB)
536
537 -def bool2subst(boolean=None, true_return=True, false_return=False, none_return=None):
538 if boolean is None:
539 return none_return
540 if boolean:
541 return true_return
542 if not boolean:
543 return false_return
544 raise ValueError('bool2subst(): <boolean> arg must be either of True, False, None')
545
546 -def bool2str(boolean=None, true_str='True', false_str='False'):
547 return bool2subst (
548 boolean = bool(boolean),
549 true_return = true_str,
550 false_return = false_str
551 )
552
553 -def none_if(value=None, none_equivalent=None, strip_string=False):
554 """Modelled after the SQL NULLIF function."""
555 if value is None:
556 return None
557 if strip_string:
558 stripped = value.strip()
559 else:
560 stripped = value
561 if stripped == none_equivalent:
562 return None
563 return value
564
565 -def coalesce(initial=None, instead=None, template_initial=None, template_instead=None, none_equivalents=None, function_initial=None):
566 """Modelled after the SQL coalesce function.
567
568 To be used to simplify constructs like:
569
570 if initial is None (or in none_equivalents):
571 real_value = (template_instead % instead) or instead
572 else:
573 real_value = (template_initial % initial) or initial
574 print real_value
575
576 @param initial: the value to be tested for <None>
577 @type initial: any Python type, must have a __str__ method if template_initial is not None
578 @param instead: the value to be returned if <initial> is None
579 @type instead: any Python type, must have a __str__ method if template_instead is not None
580 @param template_initial: if <initial> is returned replace the value into this template, must contain one <%s>
581 @type template_initial: string or None
582 @param template_instead: if <instead> is returned replace the value into this template, must contain one <%s>
583 @type template_instead: string or None
584
585 example:
586 function_initial = ('strftime', '%Y-%m-%d')
587
588 Ideas:
589 - list of insteads: initial, [instead, template], [instead, template], [instead, template], template_initial, ...
590 """
591 if none_equivalents is None:
592 none_equivalents = [None]
593
594 if initial in none_equivalents:
595
596 if template_instead is None:
597 return instead
598
599 return template_instead % instead
600
601 if function_initial is not None:
602 funcname, args = function_initial
603 func = getattr(initial, funcname)
604 initial = func(args)
605
606 if template_initial is None:
607 return initial
608
609 try:
610 return template_initial % initial
611 except TypeError:
612 return template_initial
613
615 val = match_obj.group(0).lower()
616 if val in ['von', 'van', 'de', 'la', 'l', 'der', 'den']:
617 return val
618 buf = list(val)
619 buf[0] = buf[0].upper()
620 for part in ['mac', 'mc', 'de', 'la']:
621 if len(val) > len(part) and val[:len(part)] == part:
622 buf[len(part)] = buf[len(part)].upper()
623 return ''.join(buf)
624
626 """Capitalize the first character but leave the rest alone.
627
628 Note that we must be careful about the locale, this may
629 have issues ! However, for UTF strings it should just work.
630 """
631 if (mode is None) or (mode == CAPS_NONE):
632 return text
633
634 if len(text) == 0:
635 return text
636
637 if mode == CAPS_FIRST:
638 if len(text) == 1:
639 return text[0].upper()
640 return text[0].upper() + text[1:]
641
642 if mode == CAPS_ALLCAPS:
643 return text.upper()
644
645 if mode == CAPS_FIRST_ONLY:
646 if len(text) == 1:
647 return text[0].upper()
648 return text[0].upper() + text[1:].lower()
649
650 if mode == CAPS_WORDS:
651 return regex.sub(ur'(\w)(\w+)', lambda x: x.group(1).upper() + x.group(2).lower(), text)
652
653 if mode == CAPS_NAMES:
654
655 return capitalize(text=text, mode=CAPS_FIRST)
656
657 print "ERROR: invalid capitalization mode: [%s], leaving input as is" % mode
658 return text
659
681
707
709 if lines is None:
710 lines = text.split(eol)
711
712 while True:
713 if lines[0].strip(eol).strip() != u'':
714 break
715 lines = lines[1:]
716
717 if return_list:
718 return lines
719
720 return eol.join(lines)
721
723 if lines is None:
724 lines = text.split(eol)
725
726 while True:
727 if lines[-1].strip(eol).strip() != u'':
728 break
729 lines = lines[:-1]
730
731 if return_list:
732 return lines
733
734 return eol.join(lines)
735
743
744 -def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'):
745 """A word-wrap function that preserves existing line breaks
746 and most spaces in the text. Expects that existing line
747 breaks are posix newlines (\n).
748 """
749 if width is None:
750 return text
751 wrapped = initial_indent + reduce (
752 lambda line, word, width=width: '%s%s%s' % (
753 line,
754 ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)],
755 word
756 ),
757 text.split(' ')
758 )
759
760 if subsequent_indent != u'':
761 wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n'))
762
763 if eol != u'\n':
764 wrapped = wrapped.replace('\n', eol)
765
766 return wrapped
767
768 -def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '):
769
770 text = text.replace(u'\r', u'')
771 lines = text.split(u'\n')
772 text = u''
773 for line in lines:
774
775 if strip_whitespace:
776 line = line.strip().strip(u'\t').strip()
777
778 if remove_empty_lines:
779 if line == u'':
780 continue
781
782 text += (u'%s%s' % (line, line_separator))
783
784 text = text.rstrip(line_separator)
785
786 if max_length is not None:
787 text = text[:max_length]
788
789 text = text.rstrip(line_separator)
790
791 return text
792
794 """check for special XML characters and transform them"""
795 return xml_tools.escape(text)
796
798 """check for special TeX characters and transform them"""
799
800 text = text.replace(u'\\', u'\\textbackslash')
801 text = text.replace(u'^', u'\\textasciicircum')
802 text = text.replace(u'~', u'\\textasciitilde')
803
804 text = text.replace(u'{', u'\\{')
805 text = text.replace(u'}', u'\\}')
806 text = text.replace(u'%', u'\\%')
807 text = text.replace(u'&', u'\\&')
808 text = text.replace(u'#', u'\\#')
809 text = text.replace(u'$', u'\\$')
810 text = text.replace(u'_', u'\\_')
811
812 if replace_known_unicode:
813
814 text = text.replace(u_euro, u'\\EUR')
815
816 return text
817
822
849
850
851
852
853
854 __icon_serpent = \
855 """x\xdae\x8f\xb1\x0e\x83 \x10\x86w\x9f\xe2\x92\x1blb\xf2\x07\x96\xeaH:0\xd6\
856 \xc1\x85\xd5\x98N5\xa5\xef?\xf5N\xd0\x8a\xdcA\xc2\xf7qw\x84\xdb\xfa\xb5\xcd\
857 \xd4\xda;\xc9\x1a\xc8\xb6\xcd<\xb5\xa0\x85\x1e\xeb\xbc\xbc7b!\xf6\xdeHl\x1c\
858 \x94\x073\xec<*\xf7\xbe\xf7\x99\x9d\xb21~\xe7.\xf5\x1f\x1c\xd3\xbdVlL\xc2\
859 \xcf\xf8ye\xd0\x00\x90\x0etH \x84\x80B\xaa\x8a\x88\x85\xc4(U\x9d$\xfeR;\xc5J\
860 \xa6\x01\xbbt9\xceR\xc8\x81e_$\x98\xb9\x9c\xa9\x8d,y\xa9t\xc8\xcf\x152\xe0x\
861 \xe9$\xf5\x07\x95\x0cD\x95t:\xb1\x92\xae\x9cI\xa8~\x84\x1f\xe0\xa3ec"""
862
864
865 paths = gmPaths(app_name = u'gnumed', wx = wx)
866
867 candidates = [
868 os.path.join(paths.system_app_data_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
869 os.path.join(paths.local_base_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
870 os.path.join(paths.system_app_data_dir, 'bitmaps', 'serpent.png'),
871 os.path.join(paths.local_base_dir, 'bitmaps', 'serpent.png')
872 ]
873
874 found_as = None
875 for candidate in candidates:
876 try:
877 open(candidate, 'r').close()
878 found_as = candidate
879 break
880 except IOError:
881 _log.debug('icon not found in [%s]', candidate)
882
883 if found_as is None:
884 _log.warning('no icon file found, falling back to builtin (ugly) icon')
885 icon_bmp_data = wx.BitmapFromXPMData(cPickle.loads(zlib.decompress(__icon_serpent)))
886 icon.CopyFromBitmap(icon_bmp_data)
887 else:
888 _log.debug('icon found in [%s]', found_as)
889 icon = wx.EmptyIcon()
890 try:
891 icon.LoadFile(found_as, wx.BITMAP_TYPE_ANY)
892 except AttributeError:
893 _log.exception(u"this platform doesn't support wx.Icon().LoadFile()")
894
895 return icon
896
897
898
899 if __name__ == '__main__':
900
901 if len(sys.argv) < 2:
902 sys.exit()
903
904 if sys.argv[1] != 'test':
905 sys.exit()
906
907
965
970
972
973 import datetime as dt
974 print coalesce(initial = dt.datetime.now(), template_initial = u'-- %s --', function_initial = ('strftime', u'%Y-%m-%d'))
975
976 print 'testing coalesce()'
977 print "------------------"
978 tests = [
979 [None, 'something other than <None>', None, None, 'something other than <None>'],
980 ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'],
981 ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'],
982 ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'],
983 [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'],
984 [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None']
985 ]
986 passed = True
987 for test in tests:
988 result = coalesce (
989 initial = test[0],
990 instead = test[1],
991 template_initial = test[2],
992 template_instead = test[3]
993 )
994 if result != test[4]:
995 print "ERROR"
996 print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3])
997 print "expected:", test[4]
998 print "received:", result
999 passed = False
1000
1001 if passed:
1002 print "passed"
1003 else:
1004 print "failed"
1005 return passed
1006
1008 print 'testing capitalize() ...'
1009 success = True
1010 pairs = [
1011
1012 [u'Boot', u'Boot', CAPS_FIRST_ONLY],
1013 [u'boot', u'Boot', CAPS_FIRST_ONLY],
1014 [u'booT', u'Boot', CAPS_FIRST_ONLY],
1015 [u'BoOt', u'Boot', CAPS_FIRST_ONLY],
1016 [u'boots-Schau', u'Boots-Schau', CAPS_WORDS],
1017 [u'boots-sChau', u'Boots-Schau', CAPS_WORDS],
1018 [u'boot camp', u'Boot Camp', CAPS_WORDS],
1019 [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES],
1020 [u'häkkönen', u'Häkkönen', CAPS_NAMES],
1021 [u'McBurney', u'McBurney', CAPS_NAMES],
1022 [u'mcBurney', u'McBurney', CAPS_NAMES],
1023 [u'blumberg', u'Blumberg', CAPS_NAMES],
1024 [u'roVsing', u'RoVsing', CAPS_NAMES],
1025 [u'Özdemir', u'Özdemir', CAPS_NAMES],
1026 [u'özdemir', u'Özdemir', CAPS_NAMES],
1027 ]
1028 for pair in pairs:
1029 result = capitalize(pair[0], pair[2])
1030 if result != pair[1]:
1031 success = False
1032 print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1])
1033
1034 if success:
1035 print "... SUCCESS"
1036
1037 return success
1038
1040 print "testing import_module_from_directory()"
1041 path = sys.argv[1]
1042 name = sys.argv[2]
1043 try:
1044 mod = import_module_from_directory(module_path = path, module_name = name)
1045 except:
1046 print "module import failed, see log"
1047 return False
1048
1049 print "module import succeeded", mod
1050 print dir(mod)
1051 return True
1052
1054 print "testing mkdir()"
1055 mkdir(sys.argv[1])
1056
1067
1069 print "testing none_if()"
1070 print "-----------------"
1071 tests = [
1072 [None, None, None],
1073 ['a', 'a', None],
1074 ['a', 'b', 'a'],
1075 ['a', None, 'a'],
1076 [None, 'a', None],
1077 [1, 1, None],
1078 [1, 2, 1],
1079 [1, None, 1],
1080 [None, 1, None]
1081 ]
1082
1083 for test in tests:
1084 if none_if(value = test[0], none_equivalent = test[1]) != test[2]:
1085 print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2])
1086
1087 return True
1088
1090 tests = [
1091 [True, 'Yes', 'Yes', 'Yes'],
1092 [False, 'OK', 'not OK', 'not OK']
1093 ]
1094 for test in tests:
1095 if bool2str(test[0], test[1], test[2]) != test[3]:
1096 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])
1097
1098 return True
1099
1101
1102 print bool2subst(True, 'True', 'False', 'is None')
1103 print bool2subst(False, 'True', 'False', 'is None')
1104 print bool2subst(None, 'True', 'False', 'is None')
1105
1112
1114 print "testing size2str()"
1115 print "------------------"
1116 tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000]
1117 for test in tests:
1118 print size2str(test)
1119
1121
1122 test = """
1123 second line\n
1124 3rd starts with tab \n
1125 4th with a space \n
1126
1127 6th
1128
1129 """
1130 print unwrap(text = test, max_length = 25)
1131
1133 test = 'line 1\nline 2\nline 3'
1134
1135 print "wrap 5-6-7 initial 0, subsequent 0"
1136 print wrap(test, 5)
1137 print
1138 print wrap(test, 6)
1139 print
1140 print wrap(test, 7)
1141 print "-------"
1142 raw_input()
1143 print "wrap 5 initial 1-1-3, subsequent 1-3-1"
1144 print wrap(test, 5, u' ', u' ')
1145 print
1146 print wrap(test, 5, u' ', u' ')
1147 print
1148 print wrap(test, 5, u' ', u' ')
1149 print "-------"
1150 raw_input()
1151 print "wrap 6 initial 1-1-3, subsequent 1-3-1"
1152 print wrap(test, 6, u' ', u' ')
1153 print
1154 print wrap(test, 6, u' ', u' ')
1155 print
1156 print wrap(test, 6, u' ', u' ')
1157 print "-------"
1158 raw_input()
1159 print "wrap 7 initial 1-1-3, subsequent 1-3-1"
1160 print wrap(test, 7, u' ', u' ')
1161 print
1162 print wrap(test, 7, u' ', u' ')
1163 print
1164 print wrap(test, 7, u' ', u' ')
1165
1167 print '%s: %s' % (sys.argv[2], file2md5(sys.argv[2]))
1168
1171
1176
1178 tests = [u'\\', u'^', u'~', u'{', u'}', u'%', u'&', u'#', u'$', u'_', u_euro]
1179 tests.append(u' '.join(tests))
1180 for test in tests:
1181 print u'%s:' % test, tex_escape_string(test)
1182
1184 fname = gpg_decrypt_file(filename = sys.argv[2], passphrase = sys.argv[3])
1185 if fname is not None:
1186 print "successfully decrypted:", fname
1187
1189 tests = [
1190 u'one line, no embedded line breaks ',
1191 u'one line\nwith embedded\nline\nbreaks\n '
1192 ]
1193 for test in tests:
1194 print 'as list:'
1195 print strip_trailing_empty_lines(text = test, eol=u'\n', return_list = True)
1196 print 'as string:'
1197 print u'>>>%s<<<' % strip_trailing_empty_lines(text = test, eol=u'\n', return_list = False)
1198 tests = [
1199 ['list', 'without', 'empty', 'trailing', 'lines'],
1200 ['list', 'with', 'empty', 'trailing', 'lines', '', ' ', '']
1201 ]
1202 for test in tests:
1203 print 'as list:'
1204 print strip_trailing_empty_lines(lines = test, eol = u'\n', return_list = True)
1205 print 'as string:'
1206 print strip_trailing_empty_lines(lines = test, eol = u'\n', return_list = False)
1207
1209 tests = [
1210 r'abc.exe',
1211 r'\abc.exe',
1212 r'c:\abc.exe',
1213 r'c:\d\abc.exe',
1214 r'/home/ncq/tmp.txt',
1215 r'~/tmp.txt',
1216 r'./tmp.txt',
1217 r'./.././tmp.txt',
1218 r'tmp.txt'
1219 ]
1220 for t in tests:
1221 print "[%s] -> [%s]" % (t, fname_stem(t))
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242 test_fname_stem()
1243
1244
1245
1246