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