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