A Discrete-Event Network Simulator
API
check-style.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 import os
4 import subprocess
5 import tempfile
6 import sys
7 import filecmp
8 import optparse
9 import shutil
10 import difflib
11 import re
12 
14  files = os.popen ('hg st -nma')
15  return [filename.strip() for filename in files]
16 
17 def copy_file(filename):
18  [tmp,pathname] = tempfile.mkstemp()
19  src = open(filename, 'r')
20  dst = open(pathname, 'w')
21  for line in src:
22  dst.write(line)
23  dst.close()
24  src.close()
25  return pathname
26 
27 # generate a temporary configuration file
29  level2 = """
30 nl_collapse_empty_body=False
31 nl_if_brace=Add
32 nl_brace_else=Add
33 nl_elseif_brace=Add
34 nl_else_brace=Add
35 nl_while_brace=Add
36 nl_do_brace=Add
37 nl_for_brace=Add
38 nl_brace_while=Add
39 nl_switch_brace=Add
40 nl_after_case=True
41 nl_namespace_brace=Remove
42 nl_after_brace_open=True
43 nl_class_leave_one_liners=False
44 nl_enum_leave_one_liners=False
45 nl_func_leave_one_liners=False
46 nl_if_leave_one_liners=False
47 nl_class_colon=Ignore
48 nl_after_access_spec=1
49 nl_after_semicolon=True
50 pos_class_colon=Lead
51 pos_class_comma=Trail
52 pos_bool=Lead
53 nl_class_init_args=Add
54 nl_template_class=Add
55 nl_class_brace=Add
56 # does not work very well
57 nl_func_type_name=Ignore
58 nl_func_scope_name=Ignore
59 nl_func_type_name_class=Ignore
60 nl_func_proto_type_name=Ignore
61 # function\\n(
62 nl_func_paren=Remove
63 nl_fdef_brace=Add
64 nl_struct_brace=Add
65 nl_enum_brace=Add
66 nl_union_brace=Add
67 mod_full_brace_do=Add
68 mod_full_brace_for=Add
69 mod_full_brace_if=Add
70 mod_full_brace_while=Add
71 mod_full_brace_for=Add
72 mod_remove_extra_semicolon=True
73 # max code width
74 #code_width=128
75 #ls_for_split_full=True
76 #ls_func_split_full=True
77 """
78  level1 = """
79 # extra spaces here and there
80 sp_brace_typedef=Add
81 sp_enum_assign=Add
82 sp_before_sparen=Add
83 sp_after_semi_for=Add
84 sp_arith=Add
85 sp_assign=Add
86 sp_compare=Add
87 sp_func_class_paren=Add
88 sp_after_type=Add
89 sp_type_func=Add
90 sp_angle_paren=Add
91 """
92  level0 = """
93 sp_func_proto_paren=Add
94 sp_func_def_paren=Add
95 sp_func_call_paren=Add
96 sp_after_semi_for=Ignore
97 sp_before_sparen=Ignore
98 sp_type_func=Ignore
99 sp_after_type=Ignore
100 nl_class_leave_one_liners=True
101 nl_enum_leave_one_liners=True
102 nl_func_leave_one_liners=True
103 nl_assign_leave_one_liners=True
104 #nl_collapse_empty_body=False
105 nl_getset_leave_one_liners=True
106 nl_if_leave_one_liners=True
107 nl_fdef_brace=Ignore
108 # finally, indentation configuration
109 indent_with_tabs=0
110 indent_namespace=false
111 indent_columns=2
112 indent_brace=2
113 indent_case_brace=2
114 indent_class=true
115 indent_class_colon=True
116 # alignment
117 indent_align_assign=False
118 align_left_shift=True
119 # comment reformating disabled
120 cmt_reflow_mode=1 # do not touch comments at all
121 cmt_indent_multi=False # really, do not touch them
122 """
123  [tmp,pathname] = tempfile.mkstemp()
124  dst = open(pathname, 'w')
125  dst.write(level0)
126  if level >= 1:
127  dst.write(level1)
128  if level >= 2:
129  dst.write(level2)
130  dst.close()
131  return pathname
132 
133 
135 
141  SRC = 1
142 
144  DST = 2
145 
147  BOTH = 3
148  def __init__(self):
149  """! Initializer
150  @param self The current class
151  @return none
152  """
153  self.__type = 0
154  self.__line = ''
155  def set_src(self,line):
156  """! Set source
157  @param self The current class
158  @param line source line
159  @return none
160  """
161  self.__type = self.SRC
162  self.__line = line
163  def set_dst(self,line):
164  """! Set destination
165  @param self The current class
166  @param line destination line
167  @return none
168  """
169  self.__type = self.DST
170  self.__line = line
171  def set_both(self,line):
172  """! Set both
173  @param self The current class
174  @param line
175  @return none
176  """
177  self.__type = self.BOTH
178  self.__line = line
179  def append_to_line(self, s):
180  """! Append to line
181  @param self The current class
182  @param s line to append
183  @return none
184  """
185  self.__line = self.__line + s
186  def line(self):
187  """! Get line
188  @param self The current class
189  @return line
190  """
191  return self.__line
192  def is_src(self):
193  """! Is source
194  @param self The current class
195  @return true if type is source
196  """
197  return self.__type == self.SRC or self.__type == self.BOTH
198  def is_dst(self):
199  """! Is destination
200  @param self The current class
201  @return true if type is destination
202  """
203  return self.__type == self.DST or self.__type == self.BOTH
204  def write(self, f):
205  """! Write to file
206  @param self The current class
207  @param f file
208  @return exception if invalid type
209  """
210  if self.__type == self.SRC:
211  f.write('-%s\n' % self.__line)
212  elif self.__type == self.DST:
213  f.write('+%s\n' % self.__line)
214  elif self.__type == self.BOTH:
215  f.write(' %s\n' % self.__line)
216  else:
217  raise Exception('invalid patch')
218 
219 
221 
231  def __init__(self, src_pos, dst_pos):
232  """! Initializer
233  @param self: this object
234  @param src_pos: source position
235  @param dst_pos: destination position
236  @return none
237  """
238  self.__lines = []
239  self.__src_pos = int(src_pos)
240  self.__dst_pos = int(dst_pos)
241  def src_start(self):
242  """! Source start function
243  @param self this object
244  @return source position
245  """
246  return self.__src_pos
247  def add_line(self,line):
248  """! Add line function
249  @param self The current class
250  @param line line to add
251  @return none
252  """
253  self.__lines.append(line)
254  def src(self):
255  """! Get source lines
256  @param self The current class
257  @return the source lines
258  """
259  src = []
260  for line in self.__lines:
261  if line.is_src():
262  src.append(line)
263  return src
264  def dst(self):
265  """! Get destination lines
266  @param self The current class
267  @return the destination lines
268  """
269  dst = []
270  for line in self.__lines:
271  if line.is_dst():
272  dst.append(line)
273  return dst
274  def src_len(self):
275  """! Get number of source lines
276  @param self The current class
277  @return number of source lines
278  """
279  return len(self.src())
280  def dst_len(self):
281  """! Get number of destinaton lines
282  @param self The current class
283  @return number of destination lines
284  """
285  return len(self.dst())
286  def write(self,f):
287  """! Write lines to file
288  @param self The current class
289  @param f: file to write to
290  @return none
291  """
292  f.write('@@ -%d,%d +%d,%d @@\n' % (self.__src_pos, self.src_len(),
293  self.__dst_pos, self.dst_len()))
294  for line in self.__lines:
295  line.write(f)
296 
297 
298 class Patch:
299 
305  def __init__(self):
306  """! Initializer
307  @param self The current class
308  @return none
309  """
310  self.__src = ''
311  self.__dst = ''
312  self.__chunks = []
313  def add_chunk(self, chunk):
314  """! Add chunk
315  @param self this object
316  @param chunk chunk
317  @return none
318  """
319  self.__chunks.append(chunk)
320  def chunks(self):
321  """! Get the chunks
322  @param self The current class
323  @return the chunks
324  """
325  return self.__chunks
326  def set_src(self,src):
327  """! Set source
328  @param self this object
329  @param src source
330  @return none
331  """
332  self.__src = src
333  def set_dst(self,dst):
334  """! Set destination
335  @param self this object
336  @param dst destintion
337  @return none
338  """
339  self.__dst = dst
340  def apply(self,filename):
341  """! Apply function
342  @param self The current class
343  @param filename file name
344  @return none
345  """
346  # XXX: not implemented
347  return
348  def write(self,f):
349  """! Write to file
350  @param self The current class
351  @param f the file
352  @return none
353  """
354  f.write('--- %s\n' % self.__src )
355  f.write('+++ %s\n' % self.__dst )
356  for chunk in self.__chunks:
357  chunk.write(f)
358 
359 def parse_patchset(generator):
360  src_file = re.compile('^--- (.*)$')
361  dst_file = re.compile('^\+\+\+ (.*)$')
362  chunk_start = re.compile('^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@')
363  src = re.compile('^-(.*)$')
364  dst = re.compile('^\+(.*)$')
365  both = re.compile('^ (.*)$')
366  patchset = []
367  current_patch = None
368  for line in generator:
369  m = src_file.search(line)
370  if m is not None:
371  current_patch = Patch()
372  patchset.append(current_patch)
373  current_patch.set_src(m.group(1))
374  continue
375  m = dst_file.search(line)
376  if m is not None:
377  current_patch.set_dst(m.group(1))
378  continue
379  m = chunk_start.search(line)
380  if m is not None:
381  current_chunk = PatchChunk(m.group(1), m.group(3))
382  current_patch.add_chunk(current_chunk)
383  continue
384  m = src.search(line)
385  if m is not None:
386  l = PatchChunkLine()
387  l.set_src(m.group(1))
388  current_chunk.add_line(l)
389  continue
390  m = dst.search(line)
391  if m is not None:
392  l = PatchChunkLine()
393  l.set_dst(m.group(1))
394  current_chunk.add_line(l)
395  continue
396  m = both.search(line)
397  if m is not None:
398  l = PatchChunkLine()
399  l.set_both(m.group(1))
400  current_chunk.add_line(l)
401  continue
402  raise Exception()
403  return patchset
404 
406  whitespace = re.compile('^(.*)([ \t]+)$')
407  patchset = parse_patchset(patch_generator)
408  for patch in patchset:
409  for chunk in patch.chunks():
410  src = chunk.src()
411  dst = chunk.dst()
412  try:
413  for i in range(0,len(src)):
414  s = src[i]
415  d = dst[i]
416  m = whitespace.search(s.line())
417  if m is not None and m.group(1) == d.line():
418  d.append_to_line(m.group(2))
419  except:
420  return patchset
421  return patchset
422 
423 
424 def indent(source, debug, level):
425  output = tempfile.mkstemp()[1]
426  # apply uncrustify
427  cfg = uncrustify_config_file(level)
428  if debug:
429  sys.stderr.write('original file=' + source + '\n')
430  sys.stderr.write('uncrustify config file=' + cfg + '\n')
431  sys.stderr.write('temporary file=' + output + '\n')
432  try:
433  uncrust = subprocess.Popen(['uncrustify', '-c', cfg, '-f', source, '-o', output],
434  stdin = subprocess.PIPE,
435  stdout = subprocess.PIPE,
436  stderr = subprocess.PIPE)
437  (out, err) = uncrust.communicate('')
438  if debug:
439  sys.stderr.write(out)
440  sys.stderr.write(err)
441  except OSError:
442  raise Exception ('uncrustify not installed')
443  # generate a diff file
444  src = open(source, 'r')
445  dst = open(output, 'r')
446  diff = difflib.unified_diff(src.readlines(), dst.readlines(),
447  fromfile=source, tofile=output)
448  src.close()
449  dst.close()
450  if debug:
451  initial_diff = tempfile.mkstemp()[1]
452  sys.stderr.write('initial diff file=' + initial_diff + '\n')
453  tmp = open(initial_diff, 'w')
454  tmp.writelines(diff)
455  tmp.close()
456  final_diff = tempfile.mkstemp()[1]
457  if level < 3:
458  patchset = remove_trailing_whitespace_changes(diff);
459  dst = open(final_diff, 'w')
460  if len(patchset) != 0:
461  patchset[0].write(dst)
462  dst.close()
463  else:
464  dst = open(final_diff, 'w')
465  dst.writelines(diff)
466  dst.close()
467 
468 
469  # apply diff file
470  if debug:
471  sys.stderr.write('final diff file=' + final_diff + '\n')
472  shutil.copyfile(source,output)
473  patch = subprocess.Popen(['patch', '-p1', '-i', final_diff, output],
474  stdin = subprocess.PIPE,
475  stdout = subprocess.PIPE,
476  stderr = subprocess.PIPE)
477  (out, err) = patch.communicate('')
478  if debug:
479  sys.stderr.write(out)
480  sys.stderr.write(err)
481  return output
482 
483 
484 
485 def indent_files(files, diff=False, debug=False, level=0, inplace=False):
486  output = []
487  for f in files:
488  dst = indent(f, debug=debug, level=level)
489  output.append([f,dst])
490 
491  # First, copy to inplace
492  if inplace:
493  for src,dst in output:
494  shutil.copyfile(dst,src)
495  return True
496 
497  # now, compare
498  failed = []
499  for src,dst in output:
500  if filecmp.cmp(src,dst) == 0:
501  failed.append([src, dst])
502  if len(failed) > 0:
503  if not diff:
504  print('Found %u badly indented files:' % len(failed))
505  for src,dst in failed:
506  print(' ' + src)
507  else:
508  for src,dst in failed:
509  s = open(src, 'r').readlines()
510  d = open(dst, 'r').readlines()
511  for line in difflib.unified_diff(s, d, fromfile=src, tofile=dst):
512  sys.stdout.write(line)
513  return False
514  return True
515 
516 def run_as_hg_hook(ui, repo, **kwargs):
517  # hack to work around mercurial < 1.3 bug
518  from mercurial import lock, error
519  lock.LockError = error.LockError
520  # actually do the work
521  files = hg_modified_files()
522  if not indent_files(files, inplace=False):
523  return True
524  return False
525 
527  parser = optparse.OptionParser()
528  parser.add_option('--debug', action='store_true', dest='debug', default=False,
529  help='Output some debugging information')
530  parser.add_option('-l', '--level', type='int', dest='level', default=0,
531  help="Level of style conformance: higher levels include all lower levels. "
532  "level=0: re-indent only. level=1: add extra spaces. level=2: insert extra newlines and "
533  "extra braces around single-line statements. level=3: remove all trailing spaces")
534  parser.add_option('--check-hg-hook', action='store_true', dest='hg_hook', default=False,
535  help='Get the list of files to check from mercurial\'s list of modified '
536  'and added files and assume that the script runs as a pretxncommit mercurial hook')
537  parser.add_option('--check-hg', action='store_true', dest='hg', default=False,
538  help="Get the list of files to check from mercurial\'s list of modified and added files")
539  parser.add_option('-f', '--check-file', action='store', dest='file', default='',
540  help="Check a single file")
541  parser.add_option('--diff', action='store_true', dest='diff', default=False,
542  help="Generate a diff on stdout of the indented files")
543  parser.add_option('-i', '--in-place', action='store_true', dest='in_place', default=False,
544  help="Indent the input files in-place")
545  (options,args) = parser.parse_args()
546  debug = options.debug
547  if options.hg_hook:
548  files = hg_modified_files()
549  if not indent_files(files, debug=options.debug,
550  level=options.level,
551  inplace=False):
552  sys.exit(1)
553  elif options.hg:
554  files = hg_modified_files()
555  indent_files(files, diff=options.diff,
556  debug=options.debug,
557  level=options.level,
558  inplace=options.in_place)
559  elif options.file != '':
560  file = options.file
561  if not os.path.exists(file) or \
562  not os.path.isfile(file):
563  print('file %s does not exist' % file)
564  sys.exit(1)
565  indent_files([file], diff=options.diff,
566  debug=options.debug,
567  level=options.level,
568  inplace=options.in_place)
569  sys.exit(0)
570 
571 if __name__ == '__main__':
572 # try:
573  run_as_main()
574 # except Exception, e:
575 # sys.stderr.write(str(e) + '\n')
576 # sys.exit(1)
def append_to_line(self, s)
Append to line.
Definition: check-style.py:179
def set_src(self, src)
Set source.
Definition: check-style.py:326
PatchChunkLine class.
Definition: check-style.py:134
def apply(self, filename)
Apply function.
Definition: check-style.py:340
def set_both(self, line)
Set both.
Definition: check-style.py:171
Patch class.
Definition: check-style.py:298
def chunks(self)
Get the chunks.
Definition: check-style.py:320
def dst(self)
Get destination lines.
Definition: check-style.py:264
def line(self)
Get line.
Definition: check-style.py:186
def indent(source, debug, level)
Definition: check-style.py:424
__dst
destination
Definition: check-style.py:311
def __init__(self, src_pos, dst_pos)
Initializer.
Definition: check-style.py:231
def add_line(self, line)
Add line function.
Definition: check-style.py:247
def copy_file(filename)
Definition: check-style.py:17
def set_src(self, line)
Set source.
Definition: check-style.py:155
def src(self)
Get source lines.
Definition: check-style.py:254
def write(self, f)
Write to file.
Definition: check-style.py:348
def run_as_hg_hook(ui, repo, kwargs)
Definition: check-style.py:516
__dst_pos
destination position
Definition: check-style.py:240
def is_src(self)
Is source.
Definition: check-style.py:192
def is_dst(self)
Is destination.
Definition: check-style.py:198
def set_dst(self, dst)
Set destination.
Definition: check-style.py:333
def hg_modified_files()
Definition: check-style.py:13
__src_pos
source position
Definition: check-style.py:239
def remove_trailing_whitespace_changes(patch_generator)
Definition: check-style.py:405
def uncrustify_config_file(level)
Definition: check-style.py:28
def dst_len(self)
Get number of destinaton lines.
Definition: check-style.py:280
def run_as_main()
Definition: check-style.py:526
int DST
Destination.
Definition: check-style.py:144
def __init__(self)
Initializer.
Definition: check-style.py:305
def write(self, f)
Write to file.
Definition: check-style.py:204
def src_start(self)
Source start function.
Definition: check-style.py:241
def set_dst(self, line)
Set destination.
Definition: check-style.py:163
def src_len(self)
Get number of source lines.
Definition: check-style.py:274
def parse_patchset(generator)
Definition: check-style.py:359
__lines
list of lines
Definition: check-style.py:238
def write(self, f)
Write lines to file.
Definition: check-style.py:286
def add_chunk(self, chunk)
Add chunk.
Definition: check-style.py:313
PatchChunk class.
Definition: check-style.py:220
def __init__(self)
Initializer.
Definition: check-style.py:148
def indent_files(files, diff=False, debug=False, level=0, inplace=False)
Definition: check-style.py:485