utils/check-style.py
author Josh Pelkey <jpelkey@gatech.edu>
Wed, 11 Aug 2010 11:37:37 -0400
changeset 6553 fb5ad9c7755a
permissions -rwxr-xr-x
update release notes and fix doxygen warnings
mathieu@5936
     1
#!/usr/bin/env python
mathieu@5936
     2
mathieu@5936
     3
import os
mathieu@5936
     4
import subprocess
mathieu@5936
     5
import tempfile
mathieu@5936
     6
import sys
mathieu@5936
     7
import filecmp
mathieu@5936
     8
import optparse
mathieu@5936
     9
import shutil
mathieu@5936
    10
import difflib
mathieu@5936
    11
import re
mathieu@5936
    12
mathieu@5936
    13
def hg_modified_files():
mathieu@5936
    14
    files = os.popen ('hg st -nma')
mathieu@5936
    15
    return [filename.strip() for filename in files]
mathieu@5936
    16
mathieu@5936
    17
def copy_file(filename):
mathieu@5936
    18
    [tmp,pathname] = tempfile.mkstemp()
mathieu@5936
    19
    src = open(filename, 'r')
mathieu@5936
    20
    dst = open(pathname, 'w')
mathieu@5936
    21
    for line in src:
mathieu@5936
    22
        dst.write(line)
mathieu@5936
    23
    dst.close()
mathieu@5936
    24
    src.close()
mathieu@5936
    25
    return pathname
mathieu@5936
    26
mathieu@5936
    27
# generate a temporary configuration file
mathieu@5936
    28
def uncrustify_config_file(level):
mathieu@5936
    29
    level2 = """
mathieu@5936
    30
nl_collapse_empty_body=False
mathieu@5936
    31
nl_if_brace=Add
mathieu@5936
    32
nl_brace_else=Add
mathieu@5936
    33
nl_elseif_brace=Add
mathieu@5936
    34
nl_else_brace=Add
mathieu@5936
    35
nl_while_brace=Add
mathieu@5936
    36
nl_do_brace=Add
mathieu@5936
    37
nl_for_brace=Add
mathieu@5936
    38
nl_brace_while=Add
mathieu@5936
    39
nl_switch_brace=Add
mathieu@5936
    40
nl_after_case=True
mathieu@5936
    41
nl_namespace_brace=Remove
mathieu@5936
    42
nl_after_brace_open=True
mathieu@5936
    43
nl_class_leave_one_liners=False
mathieu@5936
    44
nl_enum_leave_one_liners=False
mathieu@5936
    45
nl_func_leave_one_liners=False
mathieu@5936
    46
nl_if_leave_one_liners=False
mathieu@5936
    47
nl_class_colon=Ignore
mathieu@5936
    48
nl_after_access_spec=1
mathieu@5936
    49
nl_after_semicolon=True
mathieu@5936
    50
pos_class_colon=Lead
mathieu@5936
    51
pos_class_comma=Trail
mathieu@5936
    52
pos_bool=Lead
mathieu@5936
    53
nl_class_init_args=Add
mathieu@5936
    54
nl_template_class=Add
mathieu@5936
    55
nl_class_brace=Add
mathieu@5936
    56
# does not work very well
mathieu@5936
    57
nl_func_type_name=Ignore
mathieu@5936
    58
nl_func_scope_name=Ignore
mathieu@5936
    59
nl_func_type_name_class=Ignore
mathieu@5936
    60
nl_func_proto_type_name=Ignore
mathieu@5936
    61
# function\\n(
mathieu@5936
    62
nl_func_paren=Remove
mathieu@5936
    63
nl_fdef_brace=Add
mathieu@5936
    64
nl_struct_brace=Add
mathieu@5936
    65
nl_enum_brace=Add
mathieu@5936
    66
nl_union_brace=Add
mathieu@5936
    67
mod_full_brace_do=Add
mathieu@5936
    68
mod_full_brace_for=Add
mathieu@5936
    69
mod_full_brace_if=Add
mathieu@5936
    70
mod_full_brace_while=Add
mathieu@5936
    71
mod_full_brace_for=Add
mathieu@5936
    72
mod_remove_extra_semicolon=True
mathieu@5936
    73
# max code width
mathieu@5936
    74
#code_width=128
mathieu@5936
    75
#ls_for_split_full=True
mathieu@5936
    76
#ls_func_split_full=True
mathieu@5936
    77
"""
mathieu@5936
    78
    level1 = """
mathieu@5936
    79
# extra spaces here and there
mathieu@5936
    80
sp_func_proto_paren=Add
mathieu@5936
    81
sp_func_def_paren=Add
mathieu@5936
    82
sp_func_call_paren=Add
mathieu@5936
    83
sp_brace_typedef=Add
mathieu@5936
    84
sp_enum_assign=Add
mathieu@5936
    85
sp_before_sparen=Add
mathieu@5936
    86
sp_after_semi_for=Add
mathieu@5936
    87
sp_arith=Add
mathieu@5936
    88
sp_assign=Add
mathieu@5936
    89
sp_compare=Add
mathieu@5936
    90
sp_cmt_cpp_start=Add
mathieu@5936
    91
sp_func_class_paren=Add
mathieu@5936
    92
sp_after_type=Add
mathieu@5936
    93
sp_type_func=Add
mathieu@5936
    94
sp_angle_paren=Add
mathieu@5936
    95
"""
mathieu@5936
    96
    level0 = """
mathieu@5936
    97
sp_after_semi_for=Ignore
mathieu@5936
    98
sp_before_sparen=Ignore
mathieu@5936
    99
sp_type_func=Ignore
mathieu@5936
   100
sp_after_type=Ignore
mathieu@5936
   101
nl_class_leave_one_liners=True
mathieu@5936
   102
nl_enum_leave_one_liners=True
mathieu@5936
   103
nl_func_leave_one_liners=True
mathieu@5936
   104
nl_assign_leave_one_liners=True
mathieu@5936
   105
#nl_collapse_empty_body=False
mathieu@5936
   106
nl_getset_leave_one_liners=True
mathieu@5936
   107
nl_if_leave_one_liners=True
mathieu@5936
   108
nl_fdef_brace=Ignore
mathieu@5936
   109
# finally, indentation configuration
mathieu@5936
   110
indent_with_tabs=0
mathieu@5936
   111
indent_namespace=false
mathieu@5936
   112
indent_columns=2
mathieu@5936
   113
indent_brace=2
mathieu@5936
   114
indent_case_brace=2
mathieu@5936
   115
indent_class=true
mathieu@5936
   116
indent_class_colon=True
mathieu@5936
   117
# alignment
mathieu@5936
   118
indent_align_assign=False
mathieu@5936
   119
align_left_shift=True
mathieu@5936
   120
# comment reformating disabled
mathieu@5936
   121
cmt_reflow_mode=1 # do not touch comments at all
mathieu@5936
   122
cmt_indent_multi=False # really, do not touch them
mathieu@5936
   123
"""
mathieu@5936
   124
    [tmp,pathname] = tempfile.mkstemp()
mathieu@5936
   125
    dst = open(pathname, 'w')
mathieu@5936
   126
    dst.write(level0)
mathieu@5936
   127
    if level >= 1:
mathieu@5936
   128
        dst.write(level1)
mathieu@5936
   129
    if level >= 2:
mathieu@5936
   130
        dst.write(level2)
mathieu@5936
   131
    dst.close()
mathieu@5936
   132
    return pathname
mathieu@5936
   133
mathieu@5936
   134
class PatchChunkLine:
mathieu@5936
   135
    SRC = 1
mathieu@5936
   136
    DST = 2
mathieu@5936
   137
    BOTH = 3
mathieu@5936
   138
    def __init__(self):
mathieu@5936
   139
        self.__type = 0
mathieu@5936
   140
        self.__line = ''
mathieu@5936
   141
    def set_src(self,line):
mathieu@5936
   142
        self.__type = self.SRC
mathieu@5936
   143
        self.__line = line
mathieu@5936
   144
    def set_dst(self,line):
mathieu@5936
   145
        self.__type = self.DST
mathieu@5936
   146
        self.__line = line
mathieu@5936
   147
    def set_both(self,line):
mathieu@5936
   148
        self.__type = self.BOTH
mathieu@5936
   149
        self.__line = line
mathieu@5936
   150
    def append_to_line(self, s):
mathieu@5936
   151
        self.__line = self.__line + s
mathieu@5936
   152
    def line(self):
mathieu@5936
   153
        return self.__line
mathieu@5936
   154
    def is_src(self):
mathieu@5936
   155
        return self.__type == self.SRC or self.__type == self.BOTH
mathieu@5936
   156
    def is_dst(self):
mathieu@5936
   157
        return self.__type == self.DST or self.__type == self.BOTH
mathieu@5936
   158
    def write(self, f):
mathieu@5936
   159
        if self.__type == self.SRC:
mathieu@5936
   160
            f.write('-%s\n' % self.__line)
mathieu@5936
   161
        elif self.__type == self.DST:
mathieu@5936
   162
            f.write('+%s\n' % self.__line)
mathieu@5936
   163
        elif self.__type == self.BOTH:
mathieu@5936
   164
            f.write(' %s\n' % self.__line)
mathieu@5936
   165
        else:
mathieu@5936
   166
            raise Exception('invalid patch')
mathieu@5936
   167
    
mathieu@5936
   168
mathieu@5936
   169
class PatchChunk:
mathieu@5936
   170
    def __init__(self, src_pos, dst_pos):
mathieu@5936
   171
        self.__lines = []
mathieu@5936
   172
        self.__src_pos = int(src_pos)
mathieu@5936
   173
        self.__dst_pos = int(dst_pos)
mathieu@5936
   174
    def src_start(self):
mathieu@5936
   175
        return self.__src_pos
mathieu@5936
   176
    def add_line(self,line):
mathieu@5936
   177
        self.__lines.append(line)
mathieu@5936
   178
    def src(self):
mathieu@5936
   179
        src = []
mathieu@5936
   180
        for line in self.__lines:
mathieu@5936
   181
            if line.is_src():
mathieu@5936
   182
                src.append(line)
mathieu@5936
   183
        return src
mathieu@5936
   184
    def dst(self):
mathieu@5936
   185
        dst = []
mathieu@5936
   186
        for line in self.__lines:
mathieu@5936
   187
            if line.is_dst():
mathieu@5936
   188
                dst.append(line)
mathieu@5936
   189
        return dst
mathieu@5936
   190
    def src_len(self):
mathieu@5936
   191
        return len(self.src())
mathieu@5936
   192
    def dst_len(self):
mathieu@5936
   193
        return len(self.dst())
mathieu@5936
   194
    def write(self,f):
mathieu@5936
   195
        f.write('@@ -%d,%d +%d,%d @@\n' % (self.__src_pos, self.src_len(),
mathieu@5936
   196
                                           self.__dst_pos, self.dst_len()))
mathieu@5936
   197
        for line in self.__lines:
mathieu@5936
   198
            line.write(f)
mathieu@5936
   199
mathieu@5936
   200
class Patch:
mathieu@5936
   201
    def __init__(self):
mathieu@5936
   202
        self.__src = ''
mathieu@5936
   203
        self.__dst = ''
mathieu@5936
   204
        self.__chunks = []
mathieu@5936
   205
    def add_chunk(self, chunk):
mathieu@5936
   206
        self.__chunks.append(chunk)
mathieu@5936
   207
    def chunks(self):
mathieu@5936
   208
        return self.__chunks
mathieu@5936
   209
    def set_src(self,src):
mathieu@5936
   210
        self.__src = src
mathieu@5936
   211
    def set_dst(self,dst):
mathieu@5936
   212
        self.__dst = dst
mathieu@5936
   213
    def apply(self,filename):
mathieu@5936
   214
        # XXX: not implemented
mathieu@5936
   215
        return
mathieu@5936
   216
    def write(self,f):
mathieu@5936
   217
        f.write('--- %s\n' % self.__src )
mathieu@5936
   218
        f.write('+++ %s\n' % self.__dst )
mathieu@5936
   219
        for chunk in self.__chunks:
mathieu@5936
   220
            chunk.write(f)
mathieu@5936
   221
mathieu@5936
   222
def parse_patchset(generator):
mathieu@5936
   223
    src_file = re.compile('^--- (.*)$')
mathieu@5936
   224
    dst_file = re.compile('^\+\+\+ (.*)$')
mathieu@5936
   225
    chunk_start = re.compile('^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@')
mathieu@5936
   226
    src = re.compile('^-(.*)$')
mathieu@5936
   227
    dst = re.compile('^\+(.*)$')
mathieu@5936
   228
    both = re.compile('^ (.*)$')
mathieu@5936
   229
    patchset = []
mathieu@5936
   230
    current_patch = None
mathieu@5936
   231
    for line in generator:
mathieu@5936
   232
        m = src_file.search(line)
mathieu@5936
   233
        if m is not None:
mathieu@5936
   234
            current_patch = Patch()
mathieu@5936
   235
            patchset.append(current_patch)
mathieu@5936
   236
            current_patch.set_src(m.group(1))
mathieu@5936
   237
            continue
mathieu@5936
   238
        m = dst_file.search(line)
mathieu@5936
   239
        if m is not None:
mathieu@5936
   240
            current_patch.set_dst(m.group(1))
mathieu@5936
   241
            continue
mathieu@5936
   242
        m = chunk_start.search(line)
mathieu@5936
   243
        if m is not None:
mathieu@5936
   244
            current_chunk = PatchChunk(m.group(1), m.group(3))
mathieu@5936
   245
            current_patch.add_chunk(current_chunk)
mathieu@5936
   246
            continue
mathieu@5936
   247
        m = src.search(line)
mathieu@5936
   248
        if m is not None:
mathieu@5936
   249
            l = PatchChunkLine()
mathieu@5936
   250
            l.set_src(m.group(1))
mathieu@5936
   251
            current_chunk.add_line(l)
mathieu@5936
   252
            continue
mathieu@5936
   253
        m = dst.search(line)
mathieu@5936
   254
        if m is not None:
mathieu@5936
   255
            l = PatchChunkLine()
mathieu@5936
   256
            l.set_dst(m.group(1))
mathieu@5936
   257
            current_chunk.add_line(l)
mathieu@5936
   258
            continue
mathieu@5936
   259
        m = both.search(line)
mathieu@5936
   260
        if m is not None:
mathieu@5936
   261
            l = PatchChunkLine()
mathieu@5936
   262
            l.set_both(m.group(1))
mathieu@5936
   263
            current_chunk.add_line(l)
mathieu@5936
   264
            continue
mathieu@5936
   265
        raise Exception()
mathieu@5936
   266
    return patchset
mathieu@5936
   267
mathieu@5936
   268
def remove_trailing_whitespace_changes(patch_generator):
mathieu@5936
   269
    whitespace = re.compile('^(.*)([ \t]+)$')
mathieu@5936
   270
    patchset = parse_patchset(patch_generator)
mathieu@5936
   271
    for patch in patchset:
mathieu@5936
   272
        for chunk in patch.chunks():
mathieu@5936
   273
            src = chunk.src()
mathieu@5936
   274
            dst = chunk.dst()
mathieu@5936
   275
            try:
mathieu@5936
   276
                for i in range(0,len(src)):
mathieu@5936
   277
                    s = src[i]
mathieu@5936
   278
                    d = dst[i]
mathieu@5936
   279
                    m = whitespace.search(s.line())
mathieu@5936
   280
                    if m is not None and m.group(1) == d.line():
mathieu@5936
   281
                        d.append_to_line(m.group(2))
mathieu@5936
   282
            except:
mathieu@5936
   283
                return patchset
mathieu@5936
   284
    return patchset
mathieu@5936
   285
mathieu@5936
   286
mathieu@5936
   287
def indent(source, debug, level):
mathieu@5936
   288
    output = tempfile.mkstemp()[1]
mathieu@5936
   289
    # apply uncrustify
mathieu@5936
   290
    cfg = uncrustify_config_file(level)
mathieu@5936
   291
    if debug:
mathieu@5936
   292
        sys.stderr.write('original file=' + source + '\n')
mathieu@5936
   293
        sys.stderr.write('uncrustify config file=' + cfg + '\n')
mathieu@5936
   294
        sys.stderr.write('temporary file=' + output + '\n')
mathieu@5936
   295
    try:
mathieu@5936
   296
        uncrust = subprocess.Popen(['uncrustify', '-c', cfg, '-f', source, '-o', output],
mathieu@5936
   297
                                   stdin = subprocess.PIPE,
mathieu@5936
   298
                                   stdout = subprocess.PIPE,
mathieu@5936
   299
                                   stderr = subprocess.PIPE)
mathieu@5936
   300
        (out, err) = uncrust.communicate('')
mathieu@5936
   301
        if debug:
mathieu@5936
   302
            sys.stderr.write(out)
mathieu@5936
   303
            sys.stderr.write(err)
mathieu@5936
   304
    except OSError:
mathieu@5936
   305
        raise Exception ('uncrustify not installed')
mathieu@5936
   306
    # generate a diff file
mathieu@5936
   307
    src = open(source, 'r')
mathieu@5936
   308
    dst = open(output, 'r')
mathieu@5936
   309
    diff = difflib.unified_diff(src.readlines(), dst.readlines(), 
mathieu@5936
   310
                                fromfile=source, tofile=output)
mathieu@5936
   311
    src.close()
mathieu@5936
   312
    dst.close()
mathieu@5936
   313
    if debug:
mathieu@5936
   314
        initial_diff = tempfile.mkstemp()[1]
mathieu@5936
   315
        sys.stderr.write('initial diff file=' + initial_diff + '\n')
mathieu@5936
   316
        tmp = open(initial_diff, 'w')
mathieu@5936
   317
        tmp.writelines(diff)
mathieu@5936
   318
        tmp.close()
mathieu@5936
   319
    final_diff = tempfile.mkstemp()[1]
mathieu@5936
   320
    if level < 3:
mathieu@5936
   321
        patchset = remove_trailing_whitespace_changes(diff);
mathieu@5936
   322
        dst = open(final_diff, 'w')
mathieu@5936
   323
        if len(patchset) != 0:
mathieu@5936
   324
            patchset[0].write(dst)
mathieu@5936
   325
        dst.close()
mathieu@5936
   326
    else:
mathieu@5936
   327
        dst = open(final_diff, 'w')
mathieu@5936
   328
        dst.writelines(diff)
mathieu@5936
   329
        dst.close()
mathieu@5936
   330
            
mathieu@5936
   331
            
mathieu@5936
   332
    # apply diff file
mathieu@5936
   333
    if debug:
mathieu@5936
   334
        sys.stderr.write('final diff file=' + final_diff + '\n')
mathieu@5936
   335
    shutil.copyfile(source,output)
mathieu@5936
   336
    patch = subprocess.Popen(['patch', '-p1', '-i', final_diff, output],
mathieu@5936
   337
                             stdin = subprocess.PIPE,
mathieu@5936
   338
                             stdout = subprocess.PIPE,
mathieu@5936
   339
                             stderr = subprocess.PIPE)
mathieu@5936
   340
    (out, err) = patch.communicate('')
mathieu@5936
   341
    if debug:
mathieu@5936
   342
        sys.stderr.write(out)
mathieu@5936
   343
        sys.stderr.write(err)
mathieu@5936
   344
    return output
mathieu@5936
   345
 
mathieu@5936
   346
mathieu@5936
   347
mathieu@5936
   348
def indent_files(files, diff=False, debug=False, level=0, inplace=False):
mathieu@5936
   349
    output = []
mathieu@5936
   350
    for f in files:
mathieu@5936
   351
        dst = indent(f, debug=debug, level=level)
mathieu@5936
   352
        output.append([f,dst])
mathieu@5936
   353
mathieu@5936
   354
    # First, copy to inplace
mathieu@5936
   355
    if inplace:
mathieu@5936
   356
        for src,dst in output:
mathieu@5936
   357
            shutil.copyfile(dst,src)
mathieu@5936
   358
        return True
mathieu@5936
   359
mathieu@5936
   360
    # now, compare
mathieu@5936
   361
    failed = []
mathieu@5936
   362
    for src,dst in output:
mathieu@5936
   363
        if filecmp.cmp(src,dst) == 0:
mathieu@5936
   364
            failed.append([src, dst])
mathieu@5936
   365
    if len(failed) > 0:
mathieu@5936
   366
        if not diff:
mathieu@5936
   367
            print 'Found %u badly indented files:' % len(failed)
mathieu@5936
   368
            for src,dst in failed:
mathieu@5936
   369
                print '  ' + src
mathieu@5936
   370
        else:
mathieu@5936
   371
            for src,dst in failed:
mathieu@5936
   372
                s = open(src, 'r').readlines()
mathieu@5936
   373
                d = open(dst, 'r').readlines()
mathieu@5936
   374
                for line in difflib.unified_diff(s, d, fromfile=src, tofile=dst):
mathieu@5936
   375
                    sys.stdout.write(line)
mathieu@5936
   376
        return False
mathieu@5936
   377
    return True
mathieu@5936
   378
mathieu@5936
   379
def run_as_hg_hook(ui, repo, **kwargs):
mathieu@5936
   380
    # hack to work around mercurial < 1.3 bug
mathieu@5936
   381
    from mercurial import lock, error
mathieu@5936
   382
    lock.LockError = error.LockError
mathieu@5936
   383
    # actually do the work
mathieu@5936
   384
    files = hg_modified_files()
mathieu@5936
   385
    if not indent_files(files, inplace=False):
mathieu@5936
   386
        return True
mathieu@5936
   387
    return False
mathieu@5936
   388
mathieu@5936
   389
def run_as_main():
mathieu@5936
   390
    parser = optparse.OptionParser()
mathieu@5936
   391
    parser.add_option('--debug', action='store_true', dest='debug', default=False,
mathieu@5936
   392
                      help='Output some debugging information')
mathieu@5936
   393
    parser.add_option('-l', '--level', type='int', dest='level', default=0,
mathieu@5936
   394
                      help="Level of style conformance: higher levels include all lower levels. "
mathieu@5936
   395
                      "level=0: re-indent only. level=1: add extra spaces. level=2: insert extra newlines and "
mathieu@5936
   396
                      "extra braces around single-line statements. level=3: remove all trailing spaces")
mathieu@5936
   397
    parser.add_option('--check-hg-hook', action='store_true', dest='hg_hook', default=False, 
mathieu@5936
   398
                      help='Get the list of files to check from mercurial\'s list of modified '
mathieu@5936
   399
                      'and added files and assume that the script runs as a pretxncommit mercurial hook')
mathieu@5936
   400
    parser.add_option('--check-hg', action='store_true', dest='hg', default=False,
mathieu@5936
   401
                      help="Get the list of files to check from mercurial\'s list of modified and added files")
mathieu@5936
   402
    parser.add_option('-f', '--check-file', action='store', dest='file', default='',
mathieu@5936
   403
                      help="Check a single file")
mathieu@5936
   404
    parser.add_option('--diff', action='store_true', dest='diff', default=False,
mathieu@5936
   405
                      help="Generate a diff on stdout of the indented files")
mathieu@5936
   406
    parser.add_option('-i', '--in-place', action='store_true', dest='in_place', default=False,
mathieu@5936
   407
                      help="Indent the input files in-place")
mathieu@5936
   408
    (options,args) = parser.parse_args()
mathieu@5936
   409
    debug = options.debug
mathieu@5936
   410
    if options.hg_hook:
mathieu@5936
   411
        files = hg_modified_files()
mathieu@5936
   412
        if not indent_files(files, debug=options.debug,
mathieu@5936
   413
                            level=options.level,
mathieu@5936
   414
                            inplace=False):
mathieu@5936
   415
            sys.exit(1)
mathieu@5936
   416
    elif options.hg:
mathieu@5936
   417
        files = hg_modified_files()
mathieu@5936
   418
        indent_files(files, diff=options.diff, 
mathieu@5936
   419
                     debug=options.debug,
mathieu@5936
   420
                     level=options.level,
mathieu@5936
   421
                     inplace=options.in_place)
mathieu@5936
   422
    elif options.file != '':
mathieu@5936
   423
        file = options.file
mathieu@5936
   424
        if not os.path.exists(file) or \
mathieu@5936
   425
                not os.path.isfile(file):
mathieu@5936
   426
            print 'file %s does not exist' % file
mathieu@5936
   427
            sys.exit(1)
mathieu@5936
   428
        indent_files([file], diff=options.diff, 
mathieu@5936
   429
                     debug=options.debug,
mathieu@5936
   430
                     level=options.level,
mathieu@5936
   431
                     inplace=options.in_place)
mathieu@5936
   432
    sys.exit(0)
mathieu@5936
   433
mathieu@5936
   434
if __name__ == '__main__':
mathieu@5936
   435
#    try:
mathieu@5936
   436
        run_as_main()
mathieu@5936
   437
#    except Exception, e:
mathieu@5936
   438
#        sys.stderr.write(str(e) + '\n')
mathieu@5936
   439
#        sys.exit(1)