1
2 __doc__ = """GNUmed general tools."""
3
4
5 __version__ = "$Revision: 1.98 $"
6 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
7 __license__ = "GPL (details at http://www.gnu.org)"
8
9
10 import re as regex, sys, os, os.path, csv, tempfile, logging, hashlib
11 import urllib2 as wget, decimal, StringIO, MimeWriter, mimetypes, mimetools
12
13
14
15 if __name__ == '__main__':
16
17 logging.basicConfig(level = logging.DEBUG)
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmI18N
20 gmI18N.activate_locale()
21 gmI18N.install_domain()
22
23 from Gnumed.pycommon import gmBorg
24
25
26 _log = logging.getLogger('gm.tools')
27 _log.info(__version__)
28
29
30 ( CAPS_NONE,
31 CAPS_FIRST,
32 CAPS_ALLCAPS,
33 CAPS_WORDS,
34 CAPS_NAMES,
35 CAPS_FIRST_ONLY
36 ) = range(6)
37
38 default_mail_sender = u'gnumed@gmx.net'
39 default_mail_receiver = u'gnumed-devel@gnu.org'
40 default_mail_server = u'mail.gmx.net'
41
42
43 u_right_double_angle_quote = u'\u00AB'
44 u_registered_trademark = u'\u00AE'
45 u_plus_minus = u'\u00B1'
46 u_left_double_angle_quote = u'\u00BB'
47 u_one_quarter = u'\u00BC'
48 u_one_half = u'\u00BD'
49 u_three_quarters = u'\u00BE'
50 u_ellipsis = u'\u2026'
51 u_left_arrow = u'\u2190'
52 u_right_arrow = u'\u2192'
53 u_corresponds_to = u'\u2258'
54 u_infinity = u'\u221E'
55 u_diameter = u'\u2300'
56 u_checkmark_crossed_out = u'\u237B'
57 u_frowning_face = u'\u2639'
58 u_smiling_face = u'\u263a'
59 u_black_heart = u'\u2665'
60 u_checkmark_thin = u'\u2713'
61 u_checkmark_thick = u'\u2714'
62 u_writing_hand = u'\u270d'
63 u_pencil_1 = u'\u270e'
64 u_pencil_2 = u'\u270f'
65 u_pencil_3 = u'\u2710'
66 u_latin_cross = u'\u271d'
67 u_replacement_character = u'\ufffd'
68
69
70 -def check_for_update(url=None, current_branch=None, current_version=None, consider_latest_branch=False):
71 """Check for new releases at <url>.
72
73 Returns (bool, text).
74 True: new release available
75 False: up to date
76 None: don't know
77 """
78 try:
79 remote_file = wget.urlopen(url)
80 except (wget.URLError, ValueError, OSError):
81 _log.exception("cannot retrieve version file from [%s]", url)
82 return (None, _('Cannot retrieve version information from:\n\n%s') % url)
83
84 _log.debug('retrieving version information from [%s]', url)
85
86 from Gnumed.pycommon import gmCfg2
87 cfg = gmCfg2.gmCfgData()
88 try:
89 cfg.add_stream_source(source = 'gm-versions', stream = remote_file)
90 except (UnicodeDecodeError):
91 remote_file.close()
92 _log.exception("cannot read version file from [%s]", url)
93 return (None, _('Cannot read version information from:\n\n%s') % url)
94
95 remote_file.close()
96
97 latest_branch = cfg.get('latest branch', 'branch', source_order = [('gm-versions', 'return')])
98 latest_release_on_latest_branch = cfg.get('branch %s' % latest_branch, 'latest release', source_order = [('gm-versions', 'return')])
99 latest_release_on_current_branch = cfg.get('branch %s' % current_branch, 'latest release', source_order = [('gm-versions', 'return')])
100
101 cfg.remove_source('gm-versions')
102
103 _log.info('current release: %s', current_version)
104 _log.info('current branch: %s', current_branch)
105 _log.info('latest release on current branch: %s', latest_release_on_current_branch)
106 _log.info('latest branch: %s', latest_branch)
107 _log.info('latest release on latest branch: %s', latest_release_on_latest_branch)
108
109
110 no_release_information_available = (
111 (
112 (latest_release_on_current_branch is None) and
113 (latest_release_on_latest_branch is None)
114 ) or (
115 not consider_latest_branch and
116 (latest_release_on_current_branch is None)
117 )
118 )
119 if no_release_information_available:
120 _log.warning('no release information available')
121 msg = _('There is no version information available from:\n\n%s') % url
122 return (None, msg)
123
124
125 if consider_latest_branch:
126 _log.debug('latest branch taken into account')
127 if current_version >= latest_release_on_latest_branch:
128 _log.debug('up to date: current version >= latest version on latest branch')
129 return (False, None)
130 if latest_release_on_latest_branch is None:
131 if current_version >= latest_release_on_current_branch:
132 _log.debug('up to date: current version >= latest version on current branch and no latest branch available')
133 return (False, None)
134 else:
135 _log.debug('latest branch not taken into account')
136 if current_version >= latest_release_on_current_branch:
137 _log.debug('up to date: current version >= latest version on current branch')
138 return (False, None)
139
140 new_release_on_current_branch_available = (
141 (latest_release_on_current_branch is not None) and
142 (latest_release_on_current_branch > current_version)
143 )
144 _log.info('%snew release on current branch available', bool2str(new_release_on_current_branch_available, '', 'no '))
145
146 new_release_on_latest_branch_available = (
147 (latest_branch is not None)
148 and
149 (
150 (latest_branch > current_branch) or (
151 (latest_branch == current_branch) and
152 (latest_release_on_latest_branch > current_version)
153 )
154 )
155 )
156 _log.info('%snew release on latest branch available', bool2str(new_release_on_latest_branch_available, '', 'no '))
157
158 if not (new_release_on_current_branch_available or new_release_on_latest_branch_available):
159 _log.debug('up to date: no new releases available')
160 return (False, None)
161
162
163 msg = _('A new version of GNUmed is available.\n\n')
164 msg += _(' Your current version: "%s"\n') % current_version
165 if consider_latest_branch:
166 if new_release_on_current_branch_available:
167 msg += u'\n'
168 msg += _(' New version: "%s"') % latest_release_on_current_branch
169 msg += u'\n'
170 msg += _(' - bug fixes only\n')
171 msg += _(' - database fixups may be needed\n')
172 if new_release_on_latest_branch_available:
173 if current_branch != latest_branch:
174 msg += u'\n'
175 msg += _(' New version: "%s"') % latest_release_on_latest_branch
176 msg += u'\n'
177 msg += _(' - bug fixes and new features\n')
178 msg += _(' - database upgrade required\n')
179 else:
180 msg += u'\n'
181 msg += _(' New version: "%s"') % latest_release_on_current_branch
182 msg += u'\n'
183 msg += _(' - bug fixes only\n')
184 msg += _(' - database fixups may be needed\n')
185
186 msg += u'\n\n'
187 msg += _(
188 'Note, however, that this version may not yet\n'
189 'be available *pre-packaged* for your system.'
190 )
191
192 msg += u'\n\n'
193 msg += _('Details are found on <http://wiki.gnumed.de>.\n')
194 msg += u'\n'
195 msg += _('Version information loaded from:\n\n %s') % url
196
197 return (True, msg)
198
200
201 print ",========================================================"
202 print "| Unhandled exception caught !"
203 print "| Type :", t
204 print "| Value:", v
205 print "`========================================================"
206 _log.critical('unhandled exception caught', exc_info = (t,v,tb))
207 sys.__excepthook__(t,v,tb)
208
209
210
211 -def mkdir(directory=None):
212 try:
213 os.makedirs(directory)
214 except OSError, e:
215 if (e.errno == 17) and not os.path.isdir(directory):
216 raise
217 return True
218
219
221
222 - def __init__(self, app_name=None, wx=None):
223 """Setup pathes.
224
225 <app_name> will default to (name of the script - .py)
226 """
227 try:
228 self.already_inited
229 return
230 except AttributeError:
231 pass
232
233 self.init_paths(app_name=app_name, wx=wx)
234 self.already_inited = True
235
236
237
239
240 if wx is None:
241 _log.debug('wxPython not available')
242 _log.debug('detecting paths directly')
243
244 if app_name is None:
245 app_name, ext = os.path.splitext(os.path.basename(sys.argv[0]))
246 _log.info('app name detected as [%s]', app_name)
247 else:
248 _log.info('app name passed in as [%s]', app_name)
249
250
251 self.__home_dir = None
252
253
254
255 self.local_base_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
256
257
258 self.working_dir = os.path.abspath(os.curdir)
259
260
261
262
263 mkdir(os.path.join(self.home_dir, '.%s' % app_name))
264 self.user_config_dir = os.path.join(self.home_dir, '.%s' % app_name)
265
266
267 try:
268 self.system_config_dir = os.path.join('/etc', app_name)
269 except ValueError:
270
271 self.system_config_dir = self.user_config_dir
272
273
274 try:
275 self.system_app_data_dir = os.path.join(sys.prefix, 'share', app_name)
276 except ValueError:
277 self.system_app_data_dir = self.local_base_dir
278
279 self.__log_paths()
280 if wx is None:
281 return True
282
283
284 _log.debug('re-detecting paths with wxPython')
285
286 std_paths = wx.StandardPaths.Get()
287 _log.info('wxPython app name is [%s]', wx.GetApp().GetAppName())
288
289
290 mkdir(os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name))
291 self.user_config_dir = os.path.join(std_paths.GetUserConfigDir(), '.%s' % app_name)
292
293
294 try:
295 tmp = std_paths.GetConfigDir()
296 if not tmp.endswith(app_name):
297 tmp = os.path.join(tmp, app_name)
298 self.system_config_dir = tmp
299 except ValueError:
300
301 pass
302
303
304
305
306 if 'wxMSW' in wx.PlatformInfo:
307 _log.warning('this platform (wxMSW) sometimes returns a broken value for the system-wide application data dir')
308 else:
309 try:
310 self.system_app_data_dir = std_paths.GetDataDir()
311 except ValueError:
312 pass
313
314 self.__log_paths()
315 return True
316
318 _log.debug('sys.argv[0]: %s', sys.argv[0])
319 _log.debug('local application base dir: %s', self.local_base_dir)
320 _log.debug('current working dir: %s', self.working_dir)
321
322 _log.debug('user home dir: %s', self.home_dir)
323 _log.debug('user-specific config dir: %s', self.user_config_dir)
324 _log.debug('system-wide config dir: %s', self.system_config_dir)
325 _log.debug('system-wide application data dir: %s', self.system_app_data_dir)
326
327
328
330 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
331 msg = '[%s:user_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
332 _log.error(msg)
333 raise ValueError(msg)
334 self.__user_config_dir = path
335
337 return self.__user_config_dir
338
339 user_config_dir = property(_get_user_config_dir, _set_user_config_dir)
340
342 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
343 msg = '[%s:system_config_dir]: invalid path [%s]' % (self.__class__.__name__, path)
344 _log.error(msg)
345 raise ValueError(msg)
346 self.__system_config_dir = path
347
349 return self.__system_config_dir
350
351 system_config_dir = property(_get_system_config_dir, _set_system_config_dir)
352
354 if not (os.access(path, os.R_OK) and os.access(path, os.X_OK)):
355 msg = '[%s:system_app_data_dir]: invalid path [%s]' % (self.__class__.__name__, path)
356 _log.error(msg)
357 raise ValueError(msg)
358 self.__system_app_data_dir = path
359
361 return self.__system_app_data_dir
362
363 system_app_data_dir = property(_get_system_app_data_dir, _set_system_app_data_dir)
364
366 raise ArgumentError('invalid to set home dir')
367
369 if self.__home_dir is not None:
370 return self.__home_dir
371
372 tmp = os.path.expanduser('~')
373 if tmp == '~':
374 _log.error('this platform does not expand ~ properly')
375 try:
376 tmp = os.environ['USERPROFILE']
377 except KeyError:
378 _log.error('cannot access $USERPROFILE in environment')
379
380 if not (
381 os.access(tmp, os.R_OK)
382 and
383 os.access(tmp, os.X_OK)
384 and
385 os.access(tmp, os.W_OK)
386 ):
387 msg = '[%s:home_dir]: invalid path [%s]' % (self.__class__.__name__, tmp)
388 _log.error(msg)
389 raise ValueError(msg)
390
391 self.__home_dir = tmp
392 return self.__home_dir
393
394 home_dir = property(_get_home_dir, _set_home_dir)
395
396 -def send_mail(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='quoted-printable', attachments=None):
397
398 if message is None:
399 return False
400
401 message = message.lstrip().lstrip('\r\n').lstrip()
402
403 if sender is None:
404 sender = default_mail_sender
405
406 if receiver is None:
407 receiver = [default_mail_receiver]
408
409 if server is None:
410 server = default_mail_server
411
412 if subject is None:
413 subject = u'gmTools.py: send_mail() test'
414
415 msg = StringIO.StringIO()
416 writer = MimeWriter.MimeWriter(msg)
417 writer.addheader('To', u', '.join(receiver))
418 writer.addheader('From', sender)
419 writer.addheader('Subject', subject[:50].replace('\r', '/').replace('\n', '/'))
420 writer.addheader('MIME-Version', '1.0')
421
422 writer.startmultipartbody('mixed')
423
424
425 part = writer.nextpart()
426 body = part.startbody('text/plain')
427 part.flushheaders()
428 body.write(message.encode(encoding))
429
430
431 if attachments is not None:
432 for a in attachments:
433 filename = os.path.basename(a[0])
434 try:
435 mtype = a[1]
436 encoding = a[2]
437 except IndexError:
438 mtype, encoding = mimetypes.guess_type(a[0])
439 if mtype is None:
440 mtype = 'application/octet-stream'
441 encoding = 'base64'
442 elif mtype == 'text/plain':
443 encoding = 'quoted-printable'
444 else:
445 encoding = 'base64'
446
447 part = writer.nextpart()
448 part.addheader('Content-Transfer-Encoding', encoding)
449 body = part.startbody("%s; name=%s" % (mtype, filename))
450 mimetools.encode(open(a[0], 'rb'), body, encoding)
451
452 writer.lastpart()
453
454 import smtplib
455 session = smtplib.SMTP(server)
456 session.set_debuglevel(debug)
457 if auth is not None:
458 session.login(auth['user'], auth['password'])
459 refused = session.sendmail(sender, receiver, msg.getvalue())
460 session.quit()
461 msg.close()
462 if len(refused) != 0:
463 _log.error("refused recipients: %s" % refused)
464 return False
465
466 return True
467
468 -def send_mail_old(sender=None, receiver=None, message=None, server=None, auth=None, debug=False, subject=None, encoding='latin1'):
469 """Send an E-Mail.
470
471 <debug>: see smtplib.set_debuglevel()
472 <auth>: {'user': ..., 'password': ...}
473 <receiver>: a list of email addresses
474 """
475 if message is None:
476 return False
477 message = message.lstrip().lstrip('\r\n').lstrip()
478
479 if sender is None:
480 sender = default_mail_sender
481
482 if receiver is None:
483 receiver = [default_mail_receiver]
484
485 if server is None:
486 server = default_mail_server
487
488 if subject is None:
489 subject = u'gmTools.py: send_mail() test'
490
491 body = u"""From: %s
492 To: %s
493 Subject: %s
494
495 %s
496 """ % (sender, u', '.join(receiver), subject[:50].replace('\r', '/').replace('\n', '/'), message)
497
498 import smtplib
499 session = smtplib.SMTP(server)
500 session.set_debuglevel(debug)
501 if auth is not None:
502 session.login(auth['user'], auth['password'])
503 refused = session.sendmail(sender, receiver, body.encode(encoding))
504 session.quit()
505 if len(refused) != 0:
506 _log.error("refused recipients: %s" % refused)
507 return False
508
509 return True
510
511
512
513 -def file2md5(filename=None, return_hex=True):
514 blocksize = 2**10 * 128
515 _log.debug('md5(%s): <%s> byte blocks', filename, blocksize)
516
517 f = open(filename, 'rb')
518
519 md5 = hashlib.md5()
520 while True:
521 data = f.read(blocksize)
522 if not data:
523 break
524 md5.update(data)
525
526 _log.debug('md5(%s): %s', filename, md5.hexdigest())
527
528 if return_hex:
529 return md5.hexdigest()
530 return md5.digest()
531
533 for line in unicode_csv_data:
534 yield line.encode(encoding)
535
536
537
538
539
541
542 try:
543 is_dict_reader = kwargs['dict']
544 del kwargs['dict']
545 if is_dict_reader is not True:
546 raise KeyError
547 csv_reader = csv.DictReader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
548 except KeyError:
549 is_dict_reader = False
550 csv_reader = csv.reader(unicode2charset_encoder(unicode_csv_data), dialect=dialect, **kwargs)
551
552 for row in csv_reader:
553
554 if is_dict_reader:
555 for key in row.keys():
556 row[key] = unicode(row[key], encoding)
557 yield row
558 else:
559 yield [ unicode(cell, encoding) for cell in row ]
560
561
563 """This introduces a race condition between the file.close() and
564 actually using the filename.
565
566 The file will not exist after calling this function.
567 """
568 if tmp_dir is not None:
569 if (
570 not os.access(tmp_dir, os.F_OK)
571 or
572 not os.access(tmp_dir, os.X_OK | os.W_OK)
573 ):
574 _log.info('cannot find temporary dir [%s], using system default', tmp_dir)
575 tmp_dir = None
576
577 kwargs = {'dir': tmp_dir}
578
579 if prefix is None:
580 kwargs['prefix'] = 'gnumed-'
581 else:
582 kwargs['prefix'] = prefix
583
584 if suffix in [None, u'']:
585 kwargs['suffix'] = '.tmp'
586 else:
587 if not suffix.startswith('.'):
588 suffix = '.' + suffix
589 kwargs['suffix'] = suffix
590
591 f = tempfile.NamedTemporaryFile(**kwargs)
592 filename = f.name
593 f.close()
594
595 return filename
596
598 """Import a module from any location."""
599
600 remove_path = always_remove_path or False
601 if module_path not in sys.path:
602 _log.info('appending to sys.path: [%s]' % module_path)
603 sys.path.append(module_path)
604 remove_path = True
605
606 _log.debug('will remove import path: %s', remove_path)
607
608 if module_name.endswith('.py'):
609 module_name = module_name[:-3]
610
611 try:
612 module = __import__(module_name)
613 except StandardError:
614 _log.exception('cannot __import__() module [%s] from [%s]' % (module_name, module_path))
615 while module_path in sys.path:
616 sys.path.remove(module_path)
617 raise
618
619 _log.info('imported module [%s] as [%s]' % (module_name, module))
620 if remove_path:
621 while module_path in sys.path:
622 sys.path.remove(module_path)
623
624 return module
625
626
627
628 _kB = 1024
629 _MB = 1024 * _kB
630 _GB = 1024 * _MB
631 _TB = 1024 * _GB
632 _PB = 1024 * _TB
633
635 if size == 1:
636 return template % _('1 Byte')
637 if size < 10 * _kB:
638 return template % _('%s Bytes') % size
639 if size < _MB:
640 return template % u'%.1f kB' % (float(size) / _kB)
641 if size < _GB:
642 return template % u'%.1f MB' % (float(size) / _MB)
643 if size < _TB:
644 return template % u'%.1f GB' % (float(size) / _GB)
645 if size < _PB:
646 return template % u'%.1f TB' % (float(size) / _TB)
647 return template % u'%.1f PB' % (float(size) / _PB)
648
649 -def bool2subst(boolean=None, true_return=True, false_return=False, none_return=None):
650 if boolean is None:
651 return none_return
652 if boolean is True:
653 return true_return
654 if boolean is False:
655 return false_return
656 raise ValueError('bool2subst(): <boolean> arg must be either of True, False, None')
657
658 -def bool2str(boolean=None, true_str='True', false_str='False'):
659 return bool2subst (
660 boolean = bool(boolean),
661 true_return = true_str,
662 false_return = false_str
663 )
664
665 -def none_if(value=None, none_equivalent=None):
666 """Modelled after the SQL NULLIF function."""
667 if value == none_equivalent:
668 return None
669 return value
670
671 -def coalesce(initial=None, instead=None, template_initial=None, template_instead=None, none_equivalents=None):
672 """Modelled after the SQL coalesce function.
673
674 To be used to simplify constructs like:
675
676 if initial is None (or in none_equivalents):
677 real_value = (template_instead % instead) or instead
678 else:
679 real_value = (template_initial % initial) or initial
680 print real_value
681
682 @param initial: the value to be tested for <None>
683 @type initial: any Python type, must have a __str__ method if template_initial is not None
684 @param instead: the value to be returned if <initial> is None
685 @type instead: any Python type, must have a __str__ method if template_instead is not None
686 @param template_initial: if <initial> is returned replace the value into this template, must contain one <%s>
687 @type template_initial: string or None
688 @param template_instead: if <instead> is returned replace the value into this template, must contain one <%s>
689 @type template_instead: string or None
690
691 Ideas:
692 - list of insteads: initial, [instead, template], [instead, template], [instead, template], template_initial, ...
693 """
694 if none_equivalents is None:
695 none_equivalents = [None]
696
697 if initial in none_equivalents:
698
699 if template_instead is None:
700 return instead
701
702 return template_instead % instead
703
704 if template_initial is None:
705 return initial
706
707 try:
708 return template_initial % initial
709 except TypeError:
710 return template_initial
711
713 val = match_obj.group(0).lower()
714 if val in ['von', 'van', 'de', 'la', 'l', 'der', 'den']:
715 return val
716 buf = list(val)
717 buf[0] = buf[0].upper()
718 for part in ['mac', 'mc', 'de', 'la']:
719 if len(val) > len(part) and val[:len(part)] == part:
720 buf[len(part)] = buf[len(part)].upper()
721 return ''.join(buf)
722
724 """Capitalize the first character but leave the rest alone.
725
726 Note that we must be careful about the locale, this may
727 have issues ! However, for UTF strings it should just work.
728 """
729 if (mode is None) or (mode == CAPS_NONE):
730 return text
731
732 if mode == CAPS_FIRST:
733 if len(text) == 1:
734 return text[0].upper()
735 return text[0].upper() + text[1:]
736
737 if mode == CAPS_ALLCAPS:
738 return text.upper()
739
740 if mode == CAPS_FIRST_ONLY:
741 if len(text) == 1:
742 return text[0].upper()
743 return text[0].upper() + text[1:].lower()
744
745 if mode == CAPS_WORDS:
746 return regex.sub(ur'(\w)(\w+)', lambda x: x.group(1).upper() + x.group(2).lower(), text)
747
748 if mode == CAPS_NAMES:
749
750 return capitalize(text=text, mode=CAPS_FIRST)
751
752 print "ERROR: invalid capitalization mode: [%s], leaving input as is" % mode
753 return text
754
776
777 -def wrap(text=None, width=None, initial_indent=u'', subsequent_indent=u'', eol=u'\n'):
778 """A word-wrap function that preserves existing line breaks
779 and most spaces in the text. Expects that existing line
780 breaks are posix newlines (\n).
781 """
782 wrapped = initial_indent + reduce (
783 lambda line, word, width=width: '%s%s%s' % (
784 line,
785 ' \n'[(len(line) - line.rfind('\n') - 1 + len(word.split('\n',1)[0]) >= width)],
786 word
787 ),
788 text.split(' ')
789 )
790
791 if subsequent_indent != u'':
792 wrapped = (u'\n%s' % subsequent_indent).join(wrapped.split('\n'))
793
794 if eol != u'\n':
795 wrapped = wrapped.replace('\n', eol)
796
797 return wrapped
798
799 -def unwrap(text=None, max_length=None, strip_whitespace=True, remove_empty_lines=True, line_separator = u' // '):
800
801 text = text.replace(u'\r', u'')
802 lines = text.split(u'\n')
803 text = u''
804 for line in lines:
805
806 if strip_whitespace:
807 line = line.strip().strip(u'\t').strip()
808
809 if remove_empty_lines:
810 if line == u'':
811 continue
812
813 text += (u'%s%s' % (line, line_separator))
814
815 text = text.rstrip(line_separator)
816
817 if max_length is not None:
818 text = text[:max_length]
819
820 text = text.rstrip(line_separator)
821
822 return text
823
825 """check for special latex-characters and transform them"""
826
827 text = text.replace(u'\\', u'$\\backslash$')
828 text = text.replace(u'{', u'\\{')
829 text = text.replace(u'}', u'\\}')
830 text = text.replace(u'%', u'\\%')
831 text = text.replace(u'&', u'\\&')
832 text = text.replace(u'#', u'\\#')
833 text = text.replace(u'$', u'\\$')
834 text = text.replace(u'_', u'\\_')
835
836 text = text.replace(u'^', u'\\verb#^#')
837 text = text.replace('~','\\verb#~#')
838
839 return text
840
841
842
843 if __name__ == '__main__':
844
845 if len(sys.argv) < 2:
846 sys.exit()
847
848 if sys.argv[1] != 'test':
849 sys.exit()
850
851
907
909 print 'testing coalesce()'
910 print "------------------"
911 tests = [
912 [None, 'something other than <None>', None, None, 'something other than <None>'],
913 ['Captain', 'Mr.', '%s.'[:4], 'Mr.', 'Capt.'],
914 ['value to test', 'test 3 failed', 'template with "%s" included', None, 'template with "value to test" included'],
915 ['value to test', 'test 4 failed', 'template with value not included', None, 'template with value not included'],
916 [None, 'initial value was None', 'template_initial: %s', None, 'initial value was None'],
917 [None, 'initial value was None', 'template_initial: %%(abc)s', None, 'initial value was None']
918 ]
919 passed = True
920 for test in tests:
921 result = coalesce (
922 initial = test[0],
923 instead = test[1],
924 template_initial = test[2],
925 template_instead = test[3]
926 )
927 if result != test[4]:
928 print "ERROR"
929 print "coalesce: (%s, %s, %s, %s)" % (test[0], test[1], test[2], test[3])
930 print "expected:", test[4]
931 print "received:", result
932 passed = False
933
934 if passed:
935 print "passed"
936 else:
937 print "failed"
938 return passed
939
941 print 'testing capitalize() ...'
942 success = True
943 pairs = [
944
945 [u'Boot', u'Boot', CAPS_FIRST_ONLY],
946 [u'boot', u'Boot', CAPS_FIRST_ONLY],
947 [u'booT', u'Boot', CAPS_FIRST_ONLY],
948 [u'BoOt', u'Boot', CAPS_FIRST_ONLY],
949 [u'boots-Schau', u'Boots-Schau', CAPS_WORDS],
950 [u'boots-sChau', u'Boots-Schau', CAPS_WORDS],
951 [u'boot camp', u'Boot Camp', CAPS_WORDS],
952 [u'fahrner-Kampe', u'Fahrner-Kampe', CAPS_NAMES],
953 [u'häkkönen', u'Häkkönen', CAPS_NAMES],
954 [u'McBurney', u'McBurney', CAPS_NAMES],
955 [u'mcBurney', u'McBurney', CAPS_NAMES],
956 [u'blumberg', u'Blumberg', CAPS_NAMES],
957 [u'roVsing', u'RoVsing', CAPS_NAMES],
958 [u'Özdemir', u'Özdemir', CAPS_NAMES],
959 [u'özdemir', u'Özdemir', CAPS_NAMES],
960 ]
961 for pair in pairs:
962 result = capitalize(pair[0], pair[2])
963 if result != pair[1]:
964 success = False
965 print 'ERROR (caps mode %s): "%s" -> "%s", expected "%s"' % (pair[2], pair[0], result, pair[1])
966
967 if success:
968 print "... SUCCESS"
969
970 return success
971
973 print "testing import_module_from_directory()"
974 path = sys.argv[1]
975 name = sys.argv[2]
976 try:
977 mod = import_module_from_directory(module_path = path, module_name = name)
978 except:
979 print "module import failed, see log"
980 return False
981
982 print "module import succeeded", mod
983 print dir(mod)
984 return True
985
987 print "testing mkdir()"
988 mkdir(sys.argv[1])
989
991 msg = u"""
992 To: %s
993 From: %s
994 Subject: gmTools test suite mail
995
996 This is a test mail from the gmTools.py module.
997 """ % (default_mail_receiver, default_mail_sender)
998 print "mail sending succeeded:", send_mail (
999 receiver = [default_mail_receiver, u'karsten.hilbert@gmx.net'],
1000 message = msg,
1001 auth = {'user': default_mail_sender, 'password': u'gnumed-at-gmx-net'},
1002 debug = True,
1003 attachments = [sys.argv[0]]
1004 )
1005
1007 print "testing gmPaths()"
1008 print "-----------------"
1009 paths = gmPaths(wx=None, app_name='gnumed')
1010 print "user config dir:", paths.user_config_dir
1011 print "system config dir:", paths.system_config_dir
1012 print "local base dir:", paths.local_base_dir
1013 print "system app data dir:", paths.system_app_data_dir
1014 print "working directory :", paths.working_dir
1015
1017 print "testing none_if()"
1018 print "-----------------"
1019 tests = [
1020 [None, None, None],
1021 ['a', 'a', None],
1022 ['a', 'b', 'a'],
1023 ['a', None, 'a'],
1024 [None, 'a', None],
1025 [1, 1, None],
1026 [1, 2, 1],
1027 [1, None, 1],
1028 [None, 1, None]
1029 ]
1030
1031 for test in tests:
1032 if none_if(value = test[0], none_equivalent = test[1]) != test[2]:
1033 print 'ERROR: none_if(%s) returned [%s], expected [%s]' % (test[0], none_if(test[0], test[1]), test[2])
1034
1035 return True
1036
1038 tests = [
1039 [True, 'Yes', 'Yes', 'Yes'],
1040 [False, 'OK', 'not OK', 'not OK']
1041 ]
1042 for test in tests:
1043 if bool2str(test[0], test[1], test[2]) != test[3]:
1044 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])
1045
1046 return True
1047
1049
1050 print bool2subst(True, 'True', 'False', 'is None')
1051 print bool2subst(False, 'True', 'False', 'is None')
1052 print bool2subst(None, 'True', 'False', 'is None')
1053
1060
1062 print "testing size2str()"
1063 print "------------------"
1064 tests = [0, 1, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000]
1065 for test in tests:
1066 print size2str(test)
1067
1069
1070 test = """
1071 second line\n
1072 3rd starts with tab \n
1073 4th with a space \n
1074
1075 6th
1076
1077 """
1078 print unwrap(text = test, max_length = 25)
1079
1081 test = 'line 1\nline 2\nline 3'
1082
1083 print "wrap 5-6-7 initial 0, subsequent 0"
1084 print wrap(test, 5)
1085 print
1086 print wrap(test, 6)
1087 print
1088 print wrap(test, 7)
1089 print "-------"
1090 raw_input()
1091 print "wrap 5 initial 1-1-3, subsequent 1-3-1"
1092 print wrap(test, 5, u' ', u' ')
1093 print
1094 print wrap(test, 5, u' ', u' ')
1095 print
1096 print wrap(test, 5, u' ', u' ')
1097 print "-------"
1098 raw_input()
1099 print "wrap 6 initial 1-1-3, subsequent 1-3-1"
1100 print wrap(test, 6, u' ', u' ')
1101 print
1102 print wrap(test, 6, u' ', u' ')
1103 print
1104 print wrap(test, 6, u' ', u' ')
1105 print "-------"
1106 raw_input()
1107 print "wrap 7 initial 1-1-3, subsequent 1-3-1"
1108 print wrap(test, 7, u' ', u' ')
1109 print
1110 print wrap(test, 7, u' ', u' ')
1111 print
1112 print wrap(test, 7, u' ', u' ')
1113
1115
1116 test_data = [
1117 ('http://www.gnumed.de/downloads/gnumed-versions.txt', None, None, False),
1118 ('file:///home/ncq/gm-versions.txt', None, None, False),
1119 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', False),
1120 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.1', True),
1121 ('file:///home/ncq/gm-versions.txt', '0.2', '0.2.8.5', True)
1122 ]
1123
1124 for test in test_data:
1125 print "arguments:", test
1126 found, msg = check_for_update(test[0], test[1], test[2], test[3])
1127 print msg
1128
1129 return
1130
1132 print '%s: %s' % (sys.argv[2], file2md5(sys.argv[2]))
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149 test_md5()
1150
1151
1152