1
2 __doc__ = """GNUmed general tools."""
3
4
5
6
7 __version__ = "$Revision: 1.98 $"
8 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
9 __license__ = "GPL (details at http://www.gnu.org)"
10
11
12 import re as regex, sys, os, os.path, csv, tempfile, logging
13 import urllib2 as wget, decimal, StringIO, MimeWriter, mimetypes, mimetools
14
15
16
17 if __name__ == '__main__':
18
19 logging.basicConfig(level = logging.DEBUG)
20 sys.path.insert(0, '../../')
21 from Gnumed.pycommon import gmI18N
22 gmI18N.activate_locale()
23 gmI18N.install_domain()
24
25 from Gnumed.pycommon import gmBorg
26
27
28 _log = logging.getLogger('gm.tools')
29 _log.info(__version__)
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 default_mail_sender = u'gnumed@gmx.net'
41 default_mail_receiver = u'gnumed-devel@gnu.org'
42 default_mail_server = u'mail.gmx.net'
43
44
45 u_right_double_angle_quote = u'\u00AB'
46 u_registered_trademark = u'\u00AE'
47 u_plus_minus = u'\u00B1'
48 u_left_double_angle_quote = u'\u00BB'
49 u_one_quarter = u'\u00BC'
50 u_one_half = u'\u00BD'
51 u_three_quarters = u'\u00BE'
52 u_ellipsis = u'\u2026'
53 u_left_arrow = u'\u2190'
54 u_right_arrow = u'\u2192'
55 u_corresponds_to = u'\u2258'
56 u_infinity = u'\u221E'
57 u_diameter = u'\u2300'
58 u_checkmark_crossed_out = u'\u237B'
59 u_frowning_face = u'\u2639'
60 u_smiling_face = u'\u263a'
61 u_black_heart = u'\u2665'
62 u_checkmark_thin = u'\u2713'
63 u_checkmark_thick = u'\u2714'
64 u_writing_hand = u'\u270d'
65 u_pencil_1 = u'\u270e'
66 u_pencil_2 = u'\u270f'
67 u_pencil_3 = u'\u2710'
68 u_latin_cross = u'\u271d'
69
70
71 -def check_for_update(url=None, current_branch=None, current_version=None, consider_latest_branch=False):
72 """Check for new releases at <url>.
73
74 Returns (bool, text).
75 True: new release available
76 False: up to date
77 None: don't know
78 """
79 try:
80 remote_file = wget.urlopen(url)
81 except (wget.URLError, ValueError, OSError):
82 _log.exception("cannot retrieve version file from [%s]", url)
83 return (None, _('Cannot retrieve version information from:\n\n%s') % url)
84
85 _log.debug('retrieving version information from [%s]', url)
86
87 from Gnumed.pycommon import gmCfg2
88 cfg = gmCfg2.gmCfgData()
89 try:
90 cfg.add_stream_source(source = 'gm-versions', stream = remote_file)
91 except (UnicodeDecodeError):
92 remote_file.close()
93 _log.exception("cannot read version file from [%s]", url)
94 return (None, _('Cannot read version information from:\n\n%s') % url)
95
96 remote_file.close()
97
98 latest_branch = cfg.get('latest branch', 'branch', source_order = [('gm-versions', 'return')])
99 latest_release_on_latest_branch = cfg.get('branch %s' % latest_branch, 'latest release', source_order = [('gm-versions', 'return')])
100 latest_release_on_current_branch = cfg.get('branch %s' % current_branch, 'latest release', source_order = [('gm-versions', 'return')])
101
102 cfg.remove_source('gm-versions')
103
104 _log.info('current release: %s', current_version)
105 _log.info('current branch: %s', current_branch)
106 _log.info('latest release on current branch: %s', latest_release_on_current_branch)
107 _log.info('latest branch: %s', latest_branch)
108 _log.info('latest release on latest branch: %s', latest_release_on_latest_branch)
109
110
111 no_release_information_available = (
112 (
113 (latest_release_on_current_branch is None) and
114 (latest_release_on_latest_branch is None)
115 ) or (
116 not consider_latest_branch and
117 (latest_release_on_current_branch is None)
118 )
119 )
120 if no_release_information_available:
121 _log.warning('no release information available')
122 msg = _('There is no version information available from:\n\n%s') % url
123 return (None, msg)
124
125
126 if consider_latest_branch:
127 _log.debug('latest branch taken into account')
128 if current_version >= latest_release_on_latest_branch:
129 _log.debug('up to date: current version >= latest version on latest branch')
130 return (False, None)
131 if latest_release_on_latest_branch is None:
132 if current_version >= latest_release_on_current_branch:
133 _log.debug('up to date: current version >= latest version on current branch and no latest branch available')
134 return (False, None)
135 else:
136 _log.debug('latest branch not taken into account')
137 if current_version >= latest_release_on_current_branch:
138 _log.debug('up to date: current version >= latest version on current branch')
139 return (False, None)
140
141 new_release_on_current_branch_available = (
142 (latest_release_on_current_branch is not None) and
143 (latest_release_on_current_branch > current_version)
144 )
145 _log.info('%snew release on current branch available', bool2str(new_release_on_current_branch_available, '', 'no '))
146
147 new_release_on_latest_branch_available = (
148 (latest_branch is not None)
149 and
150 (
151 (latest_branch > current_branch) or (
152 (latest_branch == current_branch) and
153 (latest_release_on_latest_branch > current_version)
154 )
155 )
156 )
157 _log.info('%snew release on latest branch available', bool2str(new_release_on_latest_branch_available, '', 'no '))
158
159 if not (new_release_on_current_branch_available or new_release_on_latest_branch_available):
160 _log.debug('up to date: no new releases available')
161 return (False, None)
162
163
164 msg = _('A new version of GNUmed is available.\n\n')
165 msg += _(' Your current version: "%s"\n') % current_version
166 if consider_latest_branch:
167 if new_release_on_current_branch_available:
168 msg += u'\n'
169 msg += _(' New version: "%s"') % latest_release_on_current_branch
170 msg += u'\n'
171 msg += _(' - bug fixes only\n')
172 msg += _(' - database fixups may be needed\n')
173 if new_release_on_latest_branch_available:
174 if current_branch != latest_branch:
175 msg += u'\n'
176 msg += _(' New version: "%s"') % latest_release_on_latest_branch
177 msg += u'\n'
178 msg += _(' - bug fixes and new features\n')
179 msg += _(' - database upgrade required\n')
180 else:
181 msg += u'\n'
182 msg += _(' New version: "%s"') % latest_release_on_current_branch
183 msg += u'\n'
184 msg += _(' - bug fixes only\n')
185 msg += _(' - database fixups may be needed\n')
186
187 msg += u'\n\n'
188 msg += _(
189 'Note, however, that this version may not yet\n'
190 'be available *pre-packaged* for your system.'
191 )
192
193 msg += u'\n\n'
194 msg += _('Details are found on <http://wiki.gnumed.de>.\n')
195 msg += u'\n'
196 msg += _('Version information loaded from:\n\n %s') % url
197
198 return (True, msg)
199
201 for line in unicode_csv_data:
202 yield line.encode(encoding)
203
204
205
206
207
209
210 try:
211 is_dict_reader = kwargs['dict']
212 del kwargs['dict']
213 if is_dict_reader is not True:
214 raise KeyError
215 csv_reader = csv.DictReader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
216 except KeyError:
217 is_dict_reader = False
218 csv_reader = csv.reader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
219
220 for row in csv_reader:
221
222 if is_dict_reader:
223 for key in row.keys():
224 row[key] = unicode(row[key], encoding)
225 yield row
226 else:
227 yield [ unicode(cell, encoding) for cell in row ]
228
229
231
232 print ",========================================================"
233 print "| Unhandled exception caught !"
234 print "| Type :", t
235 print "| Value:", v
236 print "`========================================================"
237 _log.critical('unhandled exception caught', exc_info = (t,v,tb))
238 sys.__excepthook__(t,v,tb)
239
241
242 - def __init__(self, app_name=None, wx=None):
243 """Setup pathes.
244
245 <app_name> will default to (name of the script - .py)
246 """
247 try:
248 self.already_inited
249 return
250 except AttributeError:
251 pass
252
253 self.init_paths(app_name=app_name, wx=wx)
254 self.already_inited = True
255
256
257
259
260 if wx is None:
261 _log.debug('wxPython not available')
262 _log.debug('detecting paths directly')
263
264 if app_name is None:
265 app_name, ext = os.path.splitext(os.path.basename(sys.argv[0]))
266 _log.info('app name detected as [%s]', app_name)
267 else:
268 _log.info('app name passed in as [%s]', app_name)
269
270
271 self.__home_dir = None
272
273
274 self.local_base_dir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..', '.'))
275
276
277 self.working_dir = os.path.abspath(os.curdir)
278
279
280
281
282 mkdir(os.path.join(self.home_dir, '.%s' % app_name))
283 self.user_config_dir = os.path.join(self.home_dir, '.%s' % app_name)
284
285
286 try:
287 self.system_config_dir = os.path.join('/etc', app_name)
288 except ValueError:
289
290 self.system_config_dir = self.user_config_dir
291
292
293 try:
294 self.system_app_data_dir = os.path.join(sys.prefix, 'share', app_name)
295 except ValueError:
296 self.system_app_data_dir = self.local_base_dir
297
298 self.__log_paths()
299 if wx is None:
300 return True
301
302
303 _log.debug('re-detecting paths with wxPython')
304
305 std_paths = wx.StandardPaths.Get()
306 _log.info('wxPython app name is [%s]', wx.GetApp().GetAppName())
307
308
309 mkdir(os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name))
310 self.user_config_dir = os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name)
311
312
313 try:
314 tmp = std_paths.GetConfigDir()
315 if not tmp.endswith(app_name):
316 tmp = os.path.join(tmp, app_name)
317 self.system_config_dir = tmp
318 except ValueError:
319
320 pass
321
322
323
324
325 if 'wxMSW' in wx.PlatformInfo:
326 _log.warning('this platform (wxMSW) sometimes returns a broken value for the system-wide application data dir')
327 else:
328 try:
329 self.system_app_data_dir = std_paths.GetDataDir()
330 except ValueError:
331 pass
332
333 self.__log_paths()
334 return True
335
337 _log.debug('sys.argv[0]: %s', sys.argv[0])
338 _log.debug('local application base dir: %s', self.local_base_dir)
339 _log.debug('current working dir: %s', self.working_dir)
340
341 _log.debug('user home dir: %s', self.home_dir)
342 _log.debug('user-specific config dir: %s', self.user_config_dir)
343 _log.debug('system-wide config dir: %s', self.system_config_dir)
344 _log.debug('system-wide application data dir: %s', self.system_app_data_dir)
345
346
347
349 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
350 msg = '[%s:user_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
351 _log.error(msg)
352 raise ValueError(msg)
353 self.__user_config_dir = path
354
356 return self.__user_config_dir
357
358 user_config_dir = property(_get_user_config_dir, _set_user_config_dir)
359
361 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
362 msg = '[%s:system_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
363 _log.error(msg)
364 raise ValueError(msg)
365 self.__system_config_dir = path
366
368 return self.__system_config_dir
369
370 system_config_dir = property(_get_system_config_dir, _set_system_config_dir)
371
373 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
374 msg = '[%s:system_app_data_dir]: invalid path [%s]' % (self.__class__.__name__, path)
375 _log.error(msg)
376 raise ValueError(msg)
377 self.__system_app_data_dir = path
378
380 return self.__system_app_data_dir
381
382 system_app_data_dir = property(_get_system_app_data_dir, _set_system_app_data_dir)
383
385 raise ArgumentError('invalid to set home dir')
386
388 if self.__home_dir is not None:
389 return self.__home_dir
390
391 tmp = os.path.expanduser('~')
392 if tmp == '~':
393 _log.error('this platform does not expand ~ properly')
394 try:
395 tmp = os.environ['USERPROFILE']
396 except KeyError:
397 _log.error('cannot access $USERPROFILE in environment')
398
399 if not (
400 os.access(tmp, os.R_OK)
401 and
402 os.access(tmp, os.X_OK)
403 and
404 os.access(tmp, os.W_OK)
405 ):
406 msg = '[%s:home_dir]: invalid path [%s]' % (self.__class__.__name__, tmp)
407 _log.error(msg)
408 raise ValueError(msg)
409
410 self.__home_dir = tmp
411 return self.__home_dir
412
413 home_dir = property(_get_home_dir, _set_home_dir)
414
415 -def send_mail(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='quoted-printable', attachments=None):
416
417 if message is None:
418 return False
419
420 message = message.lstrip().lstrip('\r\n').lstrip()
421
422 if sender is None:
423 sender = default_mail_sender
424
425 if receiver is None:
426 receiver = [default_mail_receiver]
427
428 if server is None:
429 server = default_mail_server
430
431 if subject is None:
432 subject = u'gmTools.py: send_mail() test'
433
434 msg = StringIO.StringIO()
435 writer = MimeWriter.MimeWriter(msg)
436 writer.addheader('To', u', '.join(receiver))
437 writer.addheader('From', sender)
438 writer.addheader('Subject', subject[:50].replace('\r', '/').replace('\n', '/'))
439 writer.addheader('MIME-Version', '1.0')
440
441 writer.startmultipartbody('mixed')
442
443
444 part = writer.nextpart()
445 body = part.startbody('text/plain')
446 part.flushheaders()
447 body.write(message.encode(encoding))
448
449
450 if attachments is not None:
451 for a in attachments:
452 filename = os.path.basename(a[0])
453 try:
454 mtype = a[1]
455 encoding = a[2]
456 except IndexError:
457 mtype, encoding = mimetypes.guess_type(a[0])
458 if mtype is None:
459 mtype = 'application/octet-stream'
460 encoding = 'base64'
461 elif mtype == 'text/plain':
462 encoding = 'quoted-printable'
463 else:
464 encoding = 'base64'
465
466 part = writer.nextpart()
467 part.addheader('Content-Transfer-Encoding', encoding)
468 body = part.startbody("%s; name=%s" % (mtype, filename))
469 mimetools.encode(open(a[0], 'rb'), body, encoding)
470
471 writer.lastpart()
472
473 import smtplib
474 session = smtplib.SMTP(server)
475 session.set_debuglevel(debug)
476 if auth is not None:
477 session.login(auth['user'], auth['password'])
478 refused = session.sendmail(sender, receiver, msg.getvalue())
479 session.quit()
480 msg.close()
481 if len(refused) != 0:
482 _log.error("refused recipients: %s" % refused)
483 return False
484
485 return True
486
487 -def send_mail_old(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='latin1'):
488 """Send an E-Mail.
489
490 <debug>: see smtplib.set_debuglevel()
491 <auth>: {'user': ..., 'password': ...}
492 <receiver>: a list of email addresses
493 """
494 if message is None:
495 return False
496 message = message.lstrip().lstrip('\r\n').lstrip()
497
498 if sender is None:
499 sender = default_mail_sender
500
501 if receiver is None:
502 receiver = [default_mail_receiver]
503
504 if server is None:
505 server = default_mail_server
506
507 if subject is None:
508 subject = u'gmTools.py: send_mail() test'
509
510 body = u"""From: %s
511 To: %s
512 Subject: %s
513
514 %s
515 """ % (sender, u', '.join(receiver), subject[:50].replace('\r', '/').replace('\n', '/'), message)
516
517 import smtplib
518 session = smtplib.SMTP(server)
519 session.set_debuglevel(debug)
520 if auth is not None:
521 session.login(auth['user'], auth['password'])
522 refused = session.sendmail(sender, receiver, body.encode(encoding))
523 session.quit()
524 if len(refused) != 0:
525 _log.error("refused recipients: %s" % refused)
526 return False
527
528 return True
529
530 -def mkdir(directory=None):
531 try:
532 os.makedirs(directory)
533 except OSError, e:
534 if (e.errno == 17) and not os.path.isdir(directory):
535 raise
536 return True
537
539 """This introduces a race condition between the file.close() and
540 actually using the filename.
541
542 The file will not exist after calling this function.
543 """
544 if tmp_dir is not None:
545 if (
546 not os.access(tmp_dir, os.F_OK)
547 or
548 not os.access(tmp_dir, os.X_OK | os.W_OK)
549 ):
550 _log.info('cannot find temporary dir [%s], using system default', tmp_dir)
551 tmp_dir = None
552
553 kwargs = {'dir': tmp_dir}
554
555 if prefix is None:
556 kwargs['prefix'] = 'gnumed-'
557 else:
558 kwargs['prefix'] = prefix
559
560 if suffix is None:
561 kwargs['suffix'] = '.tmp'
562 else:
563 if not suffix.startswith('.'):
564 suffix = '.' + suffix
565 kwargs['suffix'] = suffix
566
567 f = tempfile.NamedTemporaryFile(**kwargs)
568 filename = f.name
569 f.close()
570
571 return filename
572
574 """Import a module from any location."""
575
576 if module_path not in sys.path:
577 _log.info('appending to sys.path: [%s]' % module_path)
578 sys.path.append(module_path)
579 remove_path = True
580 else:
581 remove_path = False
582
583 if module_name.endswith('.py'):
584 module_name = module_name[:-3]
585
586 try:
587 module = __import__(module_name)
588 except StandardError:
589 _log.exception('cannot __import__() module [%s] from [%s]' % (module_name, module_path))
590 sys.path.remove(module_path)
591 raise
592
593 _log.info('imported module [%s] as [%s]' % (module_name, module))
594 if remove_path:
595 sys.path.remove(module_path)
596
597 return module
598
599
600
601 _kB = 1024
602 _MB = 1024 * _kB
603 _GB = 1024 * _MB
604 _TB = 1024 * _GB
605 _PB = 1024 * _TB
606
608 if size == 1:
609 return template % _('1 Byte')
610 if size < 10 * _kB:
611 return template % _('%s Bytes') % size
612 if size < _MB:
613 return template % u'%.1f kB' % (float(size) / _kB)
614 if size < _GB:
615 return template % u'%.1f MB' % (float(size) / _MB)
616 if size < _TB:
617 return template % u'%.1f GB' % (float(size) / _GB)
618 if size < _PB:
619 return template % u'%.1f TB' % (float(size) / _TB)
620 return template % u'%.1f PB' % (float(size) / _PB)
621
622 -def bool2subst(boolean=None, true_return=True, false_return=False, none_return=None):
623 if boolean is None:
624 return none_return
625 if boolean is True:
626 return true_return
627 if boolean is False:
628 return false_return
629 raise ValueError('bool2subst(): <boolean> arg must be either of True, False, None')
630
631 -def bool2str(boolean=None, true_str='True', false_str='False'):
632 return bool2subst (
633 boolean = bool(boolean),
634 true_return = true_str,
635 false_return = false_str
636 )
637
638 -def none_if(value=None, none_equivalent=None):
639 """Modelled after the SQL NULLIF function."""
640 if value == none_equivalent:
641 return None
642 return value
643
644 -def coalesce(initial=None, instead=None, template_initial=None, template_instead=None, none_equivalents=None):
645 """Modelled after the SQL coalesce function.
646
647 To be used to simplify constructs like:
648
649 if initial is None (or in none_equivalents):
650 real_value = (template_instead % instead) or instead
651 else:
652 real_value = (template_initial % initial) or initial
653 print real_value
654
655 @param initial: the value to be tested for <None>
656 @type initial: any Python type, must have a __str__ method if template_initial is not None
657 @param instead: the value to be returned if <initial> is None
658 @type instead: any Python type, must have a __str__ method if template_instead is not None
659 @param template_initial: if <initial> is returned replace the value into this template, must contain one <%s>
660 @type template_initial: string or None
661 @param template_instead: if <instead> is returned replace the value into this template, must contain one <%s>
662 @type template_instead: string or None
663
664 Ideas:
665 - list of insteads: initial, [instead, template], [instead, template], [instead, template], template_initial, ...
666 """
667 if none_equivalents is None:
668 none_equivalents = [None]
669
670 if initial in none_equivalents:
671
672 if template_instead is None:
673 return instead
674
675 return template_instead % instead
676
677 if template_initial is None:
678 return initial
679
680 try:
681 return template_initial % initial
682 except TypeError:
683 return template_initial
684
686 val = match_obj.group(0).lower()
687 if val in ['von', 'van', 'de', 'la', 'l', 'der', 'den']:
688 return val
689 buf = list(val)
690 buf[0] = buf[0].upper()
691 for part in ['mac', 'mc', 'de', 'la']:
692 if len(val) > len(part) and val[:len(part)] == part:
693 buf[len(part)] = buf[len(part)].upper()
694 return ''.join(buf)
695
697 """Capitalize the first character but leave the rest alone.
698
699 Note that we must be careful about the locale, this may
700 have issues ! However, for UTF strings it should just work.
701 """
702 if (mode is None) or (mode == CAPS_NONE):
703 return text
704
705 if mode == CAPS_FIRST:
706 if len(text) == 1:
707 return text[0].upper()
708 return text[0].upper() + text[1:]
709
710 if mode == CAPS_ALLCAPS:
711 return text.upper()
712
713 if mode == CAPS_FIRST_ONLY:
714 if len(text) == 1:
715 return text[0].upper()
716 return text[0].upper() + text[1:].lower()
717
718 if mode == CAPS_WORDS:
719 return regex.sub(ur'(\w)(\w+)', lambda x: x.group(1).upper() + x.group(2).lower(), text)
720
721 if mode == CAPS_NAMES:
722
723 return capitalize(text=text, mode=CAPS_FIRST)
724
725 print "ERROR: invalid capitalization mode: [%s], leaving input as is" % mode
726 return text
727
749
750 -def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'):
751 """A word-wrap function that preserves existing line breaks
752 and most spaces in the text. Expects that existing line
753 breaks are posix newlines (\n).
754 """
755 wrapped = initial_indent + reduce (
756 lambda line, word, width=width: '%s%s%s' % (
757 line,
758 ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)],
759 word
760 ),
761 text.split(' ')
762 )
763
764 if subsequent_indent != u'':
765 wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n'))
766
767 if eol != u'\n':
768 wrapped = wrapped.replace('\n', eol)
769
770 return wrapped
771
772 -def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '):
773
774 text = text.replace(u'\r', u'')
775 lines = text.split(u'\n')
776 text = u''
777 for line in lines:
778
779 if strip_whitespace:
780 line = line.strip().strip(u'\t').strip()
781
782 if remove_empty_lines:
783 if line == u'':
784 continue
785
786 text += (u'%s%s' % (line, line_separator))
787
788 text = text.rstrip(line_separator)
789
790 if max_length is not None:
791 text = text[:max_length]
792
793 text = text.rstrip(line_separator)
794
795 return text
796
798 """check for special latex-characters and transform them"""
799
800 text = text.replace(u'\\', u'$\\backslash$')
801 text = text.replace(u'{', u'\\{')
802 text = text.replace(u'}', u'\\}')
803 text = text.replace(u'%', u'\\%')
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
809 text = text.replace(u'^', u'\\verb#^#')
810 text = text.replace('~','\\verb#~#')
811
812 return text
813
814
815
816 if __name__ == '__main__':
817
818
874
876 print 'testing coalesce()'
877 print "------------------"
878 tests = [
879 [None, 'something other than <None>', None, None, 'something other than <None>'],
880 ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'],
881 ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'],
882 ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'],
883 [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'],
884 [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None']
885 ]
886 passed = True
887 for test in tests:
888 result = coalesce (
889 initial = test[0],
890 instead = test[1],
891 template_initial = test[2],
892 template_instead = test[3]
893 )
894 if result != test[4]:
895 print "ERROR"
896 print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3])
897 print "expected:", test[4]
898 print "received:", result
899 passed = False
900
901 if passed:
902 print "passed"
903 else:
904 print "failed"
905 return passed
906
908 print 'testing capitalize() ...'
909 success = True
910 pairs = [
911
912 [u'Boot', u'Boot', CAPS_FIRST_ONLY],
913 [u'boot', u'Boot', CAPS_FIRST_ONLY],
914 [u'booT', u'Boot', CAPS_FIRST_ONLY],
915 [u'BoOt', u'Boot', CAPS_FIRST_ONLY],
916 [u'boots-Schau', u'Boots-Schau', CAPS_WORDS],
917 [u'boots-sChau', u'Boots-Schau', CAPS_WORDS],
918 [u'boot camp', u'Boot Camp', CAPS_WORDS],
919 [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES],
920 [u'häkkönen', u'Häkkönen', CAPS_NAMES],
921 [u'McBurney', u'McBurney', CAPS_NAMES],
922 [u'mcBurney', u'McBurney', CAPS_NAMES],
923 [u'blumberg', u'Blumberg', CAPS_NAMES],
924 [u'roVsing', u'RoVsing', CAPS_NAMES],
925 [u'Özdemir', u'Özdemir', CAPS_NAMES],
926 [u'özdemir', u'Özdemir', CAPS_NAMES],
927 ]
928 for pair in pairs:
929 result = capitalize(pair[0], pair[2])
930 if result != pair[1]:
931 success = False
932 print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1])
933
934 if success:
935 print "... SUCCESS"
936
937 return success
938
940 print "testing import_module_from_directory()"
941 path = sys.argv[1]
942 name = sys.argv[2]
943 try:
944 mod = import_module_from_directory(module_path = path, module_name = name)
945 except:
946 print "module import failed, see log"
947 return False
948
949 print "module import succeeded", mod
950 print dir(mod)
951 return True
952
954 print "testing mkdir()"
955 mkdir(sys.argv[1])
956
958 msg = u"""
959 To: %s
960 From: %s
961 Subject: gmTools test suite mail
962
963 This is a test mail from the gmTools.py module.
964 """ % (default_mail_receiver, default_mail_sender)
965 print "mail sending succeeded:", send_mail (
966 receiver = [default_mail_receiver, u'karsten.hilbert@gmx.net'],
967 message = msg,
968 auth = {'user': default_mail_sender, 'password': u'gnumed-at-gmx-net'},
969 debug = True,
970 attachments = [sys.argv[0]]
971 )
972
974 print "testing gmPaths()"
975 print "-----------------"
976 paths = gmPaths(wx=None, app_name='gnumed')
977 print "user config dir:", paths.user_config_dir
978 print "system config dir:", paths.system_config_dir
979 print "local base dir:", paths.local_base_dir
980 print "system app data dir:", paths.system_app_data_dir
981 print "working directory :", paths.working_dir
982
984 print "testing none_if()"
985 print "-----------------"
986 tests = [
987 [None, None, None],
988 ['a', 'a', None],
989 ['a', 'b', 'a'],
990 ['a', None, 'a'],
991 [None, 'a', None],
992 [1, 1, None],
993 [1, 2, 1],
994 [1, None, 1],
995 [None, 1, None]
996 ]
997
998 for test in tests:
999 if none_if(value = test[0], none_equivalent = test[1]) != test[2]:
1000 print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2])
1001
1002 return True
1003
1005 tests = [
1006 [True, 'Yes', 'Yes', 'Yes'],
1007 [False, 'OK', 'not OK', 'not OK']
1008 ]
1009 for test in tests:
1010 if bool2str(test[0], test[1], test[2]) != test[3]:
1011 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])
1012
1013 return True
1014
1016
1017 print bool2subst(True, 'True', 'False', 'is None')
1018 print bool2subst(False, 'True', 'False', 'is None')
1019 print bool2subst(None, 'True', 'False', 'is None')
1020
1027
1029 print "testing size2str()"
1030 print "------------------"
1031 tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000]
1032 for test in tests:
1033 print size2str(test)
1034
1036
1037 test = """
1038 second line\n
1039 3rd starts with tab \n
1040 4th with a space \n
1041
1042 6th
1043
1044 """
1045 print unwrap(text = test, max_length = 25)
1046
1048 test = 'line 1\nline 2\nline 3'
1049
1050 print "wrap 5-6-7 initial 0, subsequent 0"
1051 print wrap(test, 5)
1052 print
1053 print wrap(test, 6)
1054 print
1055 print wrap(test, 7)
1056 print "-------"
1057 raw_input()
1058 print "wrap 5 initial 1-1-3, subsequent 1-3-1"
1059 print wrap(test, 5, u' ', u' ')
1060 print
1061 print wrap(test, 5, u' ', u' ')
1062 print
1063 print wrap(test, 5, u' ', u' ')
1064 print "-------"
1065 raw_input()
1066 print "wrap 6 initial 1-1-3, subsequent 1-3-1"
1067 print wrap(test, 6, u' ', u' ')
1068 print
1069 print wrap(test, 6, u' ', u' ')
1070 print
1071 print wrap(test, 6, u' ', u' ')
1072 print "-------"
1073 raw_input()
1074 print "wrap 7 initial 1-1-3, subsequent 1-3-1"
1075 print wrap(test, 7, u' ', u' ')
1076 print
1077 print wrap(test, 7, u' ', u' ')
1078 print
1079 print wrap(test, 7, u' ', u' ')
1080
1082
1083 test_data = [
1084 ('http://www.gnumed.de/downloads/gnumed-versions.txt', None, None, False),
1085 ('file:///home/ncq/gm-versions.txt', None, None, False),
1086 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', False),
1087 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', True),
1088 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.5', True)
1089 ]
1090
1091 for test in test_data:
1092 print "arguments:", test
1093 found, msg = check_for_update(test[0], test[1], test[2], test[3])
1094 print msg
1095
1096 return
1097
1098 if len(sys.argv) > 1 and sys.argv[1] == 'test':
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114 test_unwrap()
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435