|  | @@ -12,6 +12,7 @@
 | 
	
		
			
				|  |  |  import os
 | 
	
		
			
				|  |  |  import re
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  | +import optparse
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  # ==============================
 | 
	
		
			
				|  |  |  # Oh, look!  It's a cruddy approximation to Knuth's elegant text wrapping
 | 
	
	
		
			
				|  | @@ -186,8 +187,49 @@ def body_parser(line):
 | 
	
		
			
				|  |  |      else:
 | 
	
		
			
				|  |  |          print "Weird line %r"%line
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +def clean_head(head):
 | 
	
		
			
				|  |  | +    return head
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def head_score(s):
 | 
	
		
			
				|  |  | +    m = re.match(r'^ +o (.*)', s)
 | 
	
		
			
				|  |  | +    if not m:
 | 
	
		
			
				|  |  | +        print >>sys.stderr, "Can't score %r"%s
 | 
	
		
			
				|  |  | +        return 99999
 | 
	
		
			
				|  |  | +    lw = m.group(1).lower()
 | 
	
		
			
				|  |  | +    if lw.startswith("security") and "feature" not in lw:
 | 
	
		
			
				|  |  | +        score = -300
 | 
	
		
			
				|  |  | +    elif lw.startswith("deprecated versions"):
 | 
	
		
			
				|  |  | +        score = -200
 | 
	
		
			
				|  |  | +    elif "build require" in lw:
 | 
	
		
			
				|  |  | +        score = -100
 | 
	
		
			
				|  |  | +    elif lw.startswith("major feature"):
 | 
	
		
			
				|  |  | +        score = 00
 | 
	
		
			
				|  |  | +    elif lw.startswith("major bug"):
 | 
	
		
			
				|  |  | +        score = 50
 | 
	
		
			
				|  |  | +    elif lw.startswith("major"):
 | 
	
		
			
				|  |  | +        score = 70
 | 
	
		
			
				|  |  | +    elif lw.startswith("minor feature"):
 | 
	
		
			
				|  |  | +        score = 200
 | 
	
		
			
				|  |  | +    elif lw.startswith("minor bug"):
 | 
	
		
			
				|  |  | +        score = 250
 | 
	
		
			
				|  |  | +    elif lw.startswith("minor"):
 | 
	
		
			
				|  |  | +        score = 270
 | 
	
		
			
				|  |  | +    else:
 | 
	
		
			
				|  |  | +        score = 1000
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if 'secur' in lw:
 | 
	
		
			
				|  |  | +        score -= 2
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if "(other)" in lw:
 | 
	
		
			
				|  |  | +        score += 2
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if '(' not in lw:
 | 
	
		
			
				|  |  | +        score -= 1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return score
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class ChangeLog(object):
 | 
	
		
			
				|  |  | -    def __init__(self):
 | 
	
		
			
				|  |  | +    def __init__(self, wrapText=True):
 | 
	
		
			
				|  |  |          self.prehead = []
 | 
	
		
			
				|  |  |          self.mainhead = None
 | 
	
		
			
				|  |  |          self.headtext = []
 | 
	
	
		
			
				|  | @@ -195,6 +237,7 @@ class ChangeLog(object):
 | 
	
		
			
				|  |  |          self.sections = []
 | 
	
		
			
				|  |  |          self.cursection = None
 | 
	
		
			
				|  |  |          self.lineno = 0
 | 
	
		
			
				|  |  | +        self.wrapText = wrapText
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def addLine(self, tp, line):
 | 
	
		
			
				|  |  |          self.lineno += 1
 | 
	
	
		
			
				|  | @@ -249,6 +292,11 @@ class ChangeLog(object):
 | 
	
		
			
				|  |  |                  self.lint_item(item_line, grafs, head_type)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def dumpGraf(self,par,indent1,indent2=-1):
 | 
	
		
			
				|  |  | +        if not self.wrapText:
 | 
	
		
			
				|  |  | +            for line in par:
 | 
	
		
			
				|  |  | +                print line
 | 
	
		
			
				|  |  | +            return
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          if indent2 == -1:
 | 
	
		
			
				|  |  |              indent2 = indent1
 | 
	
		
			
				|  |  |          text = " ".join(re.sub(r'\s+', ' ', line.strip()) for line in par)
 | 
	
	
		
			
				|  | @@ -258,6 +306,22 @@ class ChangeLog(object):
 | 
	
		
			
				|  |  |                                initial_indent=" "*indent1,
 | 
	
		
			
				|  |  |                                subsequent_indent=" "*indent2))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    def collateAndSortSections(self):
 | 
	
		
			
				|  |  | +        heads = []
 | 
	
		
			
				|  |  | +        sectionsByHead = { }
 | 
	
		
			
				|  |  | +        for _, head, items in self.sections:
 | 
	
		
			
				|  |  | +            head = clean_head(head)
 | 
	
		
			
				|  |  | +            try:
 | 
	
		
			
				|  |  | +                s = sectionsByHead[head]
 | 
	
		
			
				|  |  | +            except KeyError:
 | 
	
		
			
				|  |  | +                s = sectionsByHead[head] = []
 | 
	
		
			
				|  |  | +                heads.append( (head_score(head), head, s) )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            s.extend(items)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        heads.sort()
 | 
	
		
			
				|  |  | +        self.sections = [ (0, head, items) for _,head,items in heads ]
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      def dump(self):
 | 
	
		
			
				|  |  |          if self.prehead:
 | 
	
		
			
				|  |  |              self.dumpGraf(self.prehead, 0)
 | 
	
	
		
			
				|  | @@ -279,20 +343,36 @@ class ChangeLog(object):
 | 
	
		
			
				|  |  |              print
 | 
	
		
			
				|  |  |          print
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -CL = ChangeLog()
 | 
	
		
			
				|  |  | -parser = head_parser
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -if len(sys.argv) == 1:
 | 
	
		
			
				|  |  | +op = optparse.OptionParser(usage="usage: %prog [options] [filename]")
 | 
	
		
			
				|  |  | +op.add_option('-W', '--no-wrap', action='store_false',
 | 
	
		
			
				|  |  | +              dest='wrapText', default=True,
 | 
	
		
			
				|  |  | +              help='Do not re-wrap paragraphs')
 | 
	
		
			
				|  |  | +op.add_option('-S', '--no-sort', action='store_false',
 | 
	
		
			
				|  |  | +              dest='sort', default=True,
 | 
	
		
			
				|  |  | +              help='Do not sort or collate sections')
 | 
	
		
			
				|  |  | +op.add_option('-o', '--output', dest='output',
 | 
	
		
			
				|  |  | +              default=None, metavar='FILE', help="write output to FILE")
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +options,args = op.parse_args()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if len(args) > 1:
 | 
	
		
			
				|  |  | +    op.error("Too many arguments")
 | 
	
		
			
				|  |  | +elif len(args) == 0:
 | 
	
		
			
				|  |  |      fname = 'ChangeLog'
 | 
	
		
			
				|  |  |  else:
 | 
	
		
			
				|  |  | -    fname = sys.argv[1]
 | 
	
		
			
				|  |  | +    fname = args[0]
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -fname_new = fname+".new"
 | 
	
		
			
				|  |  | +if options.output == None:
 | 
	
		
			
				|  |  | +    options.output = fname
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -sys.stdin = open(fname, 'r')
 | 
	
		
			
				|  |  | +if fname != '-':
 | 
	
		
			
				|  |  | +    sys.stdin = open(fname, 'r')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  nextline = None
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +CL = ChangeLog(wrapText=options.wrapText)
 | 
	
		
			
				|  |  | +parser = head_parser
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  for line in sys.stdin:
 | 
	
		
			
				|  |  |      line = line.rstrip()
 | 
	
		
			
				|  |  |      tp = parser(line)
 | 
	
	
		
			
				|  | @@ -307,7 +387,15 @@ for line in sys.stdin:
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  CL.lint()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -sys.stdout = open(fname_new, 'w')
 | 
	
		
			
				|  |  | +if options.output != '-':
 | 
	
		
			
				|  |  | +    fname_new = options.output+".new"
 | 
	
		
			
				|  |  | +    fname_out = options.output
 | 
	
		
			
				|  |  | +    sys.stdout = open(fname_new, 'w')
 | 
	
		
			
				|  |  | +else:
 | 
	
		
			
				|  |  | +    fname_new = fname_out = None
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +if options.sort:
 | 
	
		
			
				|  |  | +    CL.collateAndSortSections()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  CL.dump()
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -317,4 +405,5 @@ if nextline is not None:
 | 
	
		
			
				|  |  |  for line in sys.stdin:
 | 
	
		
			
				|  |  |      sys.stdout.write(line)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -os.rename(fname_new, fname)
 | 
	
		
			
				|  |  | +if fname_new is not None:
 | 
	
		
			
				|  |  | +    os.rename(fname_new, fname_out)
 |