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
736 -def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'):
737 """A word-wrap function that preserves existing line breaks
738 and most spaces in the text. Expects that existing line
739 breaks are posix newlines (\n).
740 """
741 if width is None:
742 return text
743 wrapped = initial_indent + reduce (
744 lambda line, word, width=width: '%s%s%s' % (
745 line,
746 ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)],
747 word
748 ),
749 text.split(' ')
750 )
751
752 if subsequent_indent != u'':
753 wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n'))
754
755 if eol != u'\n':
756 wrapped = wrapped.replace('\n', eol)
757
758 return wrapped
759
760 -def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '):
761
762 text = text.replace(u'\r', u'')
763 lines = text.split(u'\n')
764 text = u''
765 for line in lines:
766
767 if strip_whitespace:
768 line = line.strip().strip(u'\t').strip()
769
770 if remove_empty_lines:
771 if line == u'':
772 continue
773
774 text += (u'%s%s' % (line, line_separator))
775
776 text = text.rstrip(line_separator)
777
778 if max_length is not None:
779 text = text[:max_length]
780
781 text = text.rstrip(line_separator)
782
783 return text
784
786 """check for special XML characters and transform them"""
787 return xml_tools.escape(text)
788
790 """check for special TeX characters and transform them"""
791
792 text = text.replace(u'\\', u'\\textbackslash')
793 text = text.replace(u'^', u'\\textasciicircum')
794 text = text.replace(u'~', u'\\textasciitilde')
795
796 text = text.replace(u'{', u'\\{')
797 text = text.replace(u'}', u'\\}')
798 text = text.replace(u'%', u'\\%')
799 text = text.replace(u'&', u'\\&')
800 text = text.replace(u'#', u'\\#')
801 text = text.replace(u'$', u'\\$')
802 text = text.replace(u'_', u'\\_')
803
804 if replace_known_unicode:
805
806 text = text.replace(u_euro, u'\\EUR')
807
808 return text
809
814
841
842
843
844
845
846 __icon_serpent = \
847 """x\xdae\x8f\xb1\x0e\x83 \x10\x86w\x9f\xe2\x92\x1blb\xf2\x07\x96\xeaH:0\xd6\
848 \xc1\x85\xd5\x98N5\xa5\xef?\xf5N\xd0\x8a\xdcA\xc2\xf7qw\x84\xdb\xfa\xb5\xcd\
849 \xd4\xda;\xc9\x1a\xc8\xb6\xcd<\xb5\xa0\x85\x1e\xeb\xbc\xbc7b!\xf6\xdeHl\x1c\
850 \x94\x073\xec<*\xf7\xbe\xf7\x99\x9d\xb21~\xe7.\xf5\x1f\x1c\xd3\xbdVlL\xc2\
851 \xcf\xf8ye\xd0\x00\x90\x0etH \x84\x80B\xaa\x8a\x88\x85\xc4(U\x9d$\xfeR;\xc5J\
852 \xa6\x01\xbbt9\xceR\xc8\x81e_$\x98\xb9\x9c\xa9\x8d,y\xa9t\xc8\xcf\x152\xe0x\
853 \xe9$\xf5\x07\x95\x0cD\x95t:\xb1\x92\xae\x9cI\xa8~\x84\x1f\xe0\xa3ec"""
854
856
857 paths = gmPaths(app_name = u'gnumed', wx = wx)
858
859 candidates = [
860 os.path.join(paths.system_app_data_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
861 os.path.join(paths.local_base_dir, 'bitmaps', 'gm_icon-serpent_and_gnu.png'),
862 os.path.join(paths.system_app_data_dir, 'bitmaps', 'serpent.png'),
863 os.path.join(paths.local_base_dir, 'bitmaps', 'serpent.png')
864 ]
865
866 found_as = None
867 for candidate in candidates:
868 try:
869 open(candidate, 'r').close()
870 found_as = candidate
871 break
872 except IOError:
873 _log.debug('icon not found in [%s]', candidate)
874
875 if found_as is None:
876 _log.warning('no icon file found, falling back to builtin (ugly) icon')
877 icon_bmp_data = wx.BitmapFromXPMData(cPickle.loads(zlib.decompress(__icon_serpent)))
878 icon.CopyFromBitmap(icon_bmp_data)
879 else:
880 _log.debug('icon found in [%s]', found_as)
881 icon = wx.EmptyIcon()
882 try:
883 icon.LoadFile(found_as, wx.BITMAP_TYPE_ANY)
884 except AttributeError:
885 _log.exception(u"this platform doesn't support wx.Icon().LoadFile()")
886
887 return icon
888
889
890
891 if __name__ == '__main__':
892
893 if len(sys.argv) < 2:
894 sys.exit()
895
896 if sys.argv[1] != 'test':
897 sys.exit()
898
899
957
962
964
965 import datetime as dt
966 print coalesce(initial = dt.datetime.now(), template_initial = u'-- %s --', function_initial = ('strftime', u'%Y-%m-%d'))
967
968 print 'testing coalesce()'
969 print "------------------"
970 tests = [
971 [None, 'something other than <None>', None, None, 'something other than <None>'],
972 ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'],
973 ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'],
974 ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'],
975 [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'],
976 [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None']
977 ]
978 passed = True
979 for test in tests:
980 result = coalesce (
981 initial = test[0],
982 instead = test[1],
983 template_initial = test[2],
984 template_instead = test[3]
985 )
986 if result != test[4]:
987 print "ERROR"
988 print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3])
989 print "expected:", test[4]
990 print "received:", result
991 passed = False
992
993 if passed:
994 print "passed"
995 else:
996 print "failed"
997 return passed
998
1000 print 'testing capitalize() ...'
1001 success = True
1002 pairs = [
1003
1004 [u'Boot', u'Boot', CAPS_FIRST_ONLY],
1005 [u'boot', u'Boot', CAPS_FIRST_ONLY],
1006 [u'booT', u'Boot', CAPS_FIRST_ONLY],
1007 [u'BoOt', u'Boot', CAPS_FIRST_ONLY],
1008 [u'boots-Schau', u'Boots-Schau', CAPS_WORDS],
1009 [u'boots-sChau', u'Boots-Schau', CAPS_WORDS],
1010 [u'boot camp', u'Boot Camp', CAPS_WORDS],
1011 [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES],
1012 [u'häkkönen', u'Häkkönen', CAPS_NAMES],
1013 [u'McBurney', u'McBurney', CAPS_NAMES],
1014 [u'mcBurney', u'McBurney', CAPS_NAMES],
1015 [u'blumberg', u'Blumberg', CAPS_NAMES],
1016 [u'roVsing', u'RoVsing', CAPS_NAMES],
1017 [u'Özdemir', u'Özdemir', CAPS_NAMES],
1018 [u'özdemir', u'Özdemir', CAPS_NAMES],
1019 ]
1020 for pair in pairs:
1021 result = capitalize(pair[0], pair[2])
1022 if result != pair[1]:
1023 success = False
1024 print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1])
1025
1026 if success:
1027 print "... SUCCESS"
1028
1029 return success
1030
1032 print "testing import_module_from_directory()"
1033 path = sys.argv[1]
1034 name = sys.argv[2]
1035 try:
1036 mod = import_module_from_directory(module_path = path, module_name = name)
1037 except:
1038 print "module import failed, see log"
1039 return False
1040
1041 print "module import succeeded", mod
1042 print dir(mod)
1043 return True
1044
1046 print "testing mkdir()"
1047 mkdir(sys.argv[1])
1048
1059
1061 print "testing none_if()"
1062 print "-----------------"
1063 tests = [
1064 [None, None, None],
1065 ['a', 'a', None],
1066 ['a', 'b', 'a'],
1067 ['a', None, 'a'],
1068 [None, 'a', None],
1069 [1, 1, None],
1070 [1, 2, 1],
1071 [1, None, 1],
1072 [None, 1, None]
1073 ]
1074
1075 for test in tests:
1076 if none_if(value = test[0], none_equivalent = test[1]) != test[2]:
1077 print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2])
1078
1079 return True
1080
1082 tests = [
1083 [True, 'Yes', 'Yes', 'Yes'],
1084 [False, 'OK', 'not OK', 'not OK']
1085 ]
1086 for test in tests:
1087 if bool2str(test[0], test[1], test[2]) != test[3]:
1088 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])
1089
1090 return True
1091
1093
1094 print bool2subst(True, 'True', 'False', 'is None')
1095 print bool2subst(False, 'True', 'False', 'is None')
1096 print bool2subst(None, 'True', 'False', 'is None')
1097
1104
1106 print "testing size2str()"
1107 print "------------------"
1108 tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000]
1109 for test in tests:
1110 print size2str(test)
1111
1113
1114 test = """
1115 second line\n
1116 3rd starts with tab \n
1117 4th with a space \n
1118
1119 6th
1120
1121 """
1122 print unwrap(text = test, max_length = 25)
1123
1125 test = 'line 1\nline 2\nline 3'
1126
1127 print "wrap 5-6-7 initial 0, subsequent 0"
1128 print wrap(test, 5)
1129 print
1130 print wrap(test, 6)
1131 print
1132 print wrap(test, 7)
1133 print "-------"
1134 raw_input()
1135 print "wrap 5 initial 1-1-3, subsequent 1-3-1"
1136 print wrap(test, 5, u' ', u' ')
1137 print
1138 print wrap(test, 5, u' ', u' ')
1139 print
1140 print wrap(test, 5, u' ', u' ')
1141 print "-------"
1142 raw_input()
1143 print "wrap 6 initial 1-1-3, subsequent 1-3-1"
1144 print wrap(test, 6, u' ', u' ')
1145 print
1146 print wrap(test, 6, u' ', u' ')
1147 print
1148 print wrap(test, 6, u' ', u' ')
1149 print "-------"
1150 raw_input()
1151 print "wrap 7 initial 1-1-3, subsequent 1-3-1"
1152 print wrap(test, 7, u' ', u' ')
1153 print
1154 print wrap(test, 7, u' ', u' ')
1155 print
1156 print wrap(test, 7, u' ', u' ')
1157
1159 print '%s: %s' % (sys.argv[2], file2md5(sys.argv[2]))
1160
1163
1168
1170 tests = [u'\\', u'^', u'~', u'{', u'}', u'%', u'&', u'#', u'$', u'_', u_euro]
1171 tests.append(u' '.join(tests))
1172 for test in tests:
1173 print u'%s:' % test, tex_escape_string(test)
1174
1176 fname = gpg_decrypt_file(filename = sys.argv[2], passphrase = sys.argv[3])
1177 if fname is not None:
1178 print "successfully decrypted:", fname
1179
1181 tests = [
1182 u'one line, no embedded line breaks ',
1183 u'one line\nwith embedded\nline\nbreaks\n '
1184 ]
1185 for test in tests:
1186 print 'as list:'
1187 print strip_trailing_empty_lines(text = test, eol=u'\n', return_list = True)
1188 print 'as string:'
1189 print u'>>>%s<<<' % strip_trailing_empty_lines(text = test, eol=u'\n', return_list = False)
1190 tests = [
1191 ['list', 'without', 'empty', 'trailing', 'lines'],
1192 ['list', 'with', 'empty', 'trailing', 'lines', '', ' ', '']
1193 ]
1194 for test in tests:
1195 print 'as list:'
1196 print strip_trailing_empty_lines(lines = test, eol = u'\n', return_list = True)
1197 print 'as string:'
1198 print strip_trailing_empty_lines(lines = test, eol = u'\n', return_list = False)
1199
1201 tests = [
1202 r'abc.exe',
1203 r'\abc.exe',
1204 r'c:\abc.exe',
1205 r'c:\d\abc.exe',
1206 r'/home/ncq/tmp.txt',
1207 r'~/tmp.txt',
1208 r'./tmp.txt',
1209 r'./.././tmp.txt',
1210 r'tmp.txt'
1211 ]
1212 for t in tests:
1213 print "[%s] -> [%s]" % (t, fname_stem(t))
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234 test_fname_stem()
1235
1236
1237
1238