Rev 5491: Merge lp:~vila/bzr/fixed-in fixing NEWS entry in http://bazaar.launchpad.net/~vila/bzr/integration/
Vincent Ladeuil
v.ladeuil+lp at free.fr
Wed Oct 13 07:33:56 BST 2010
At http://bazaar.launchpad.net/~vila/bzr/integration/
------------------------------------------------------------
revno: 5491 [merge]
revision-id: v.ladeuil+lp at free.fr-20101013063355-fwb60yy1cmy748h9
parent: pqm at pqm.ubuntu.com-20101013043153-ki2mvt5miuxbun8l
parent: v.ladeuil+lp at free.fr-20100928090258-tf419g5olimxuagl
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: trunk
timestamp: Wed 2010-10-13 08:33:55 +0200
message:
Merge lp:~vila/bzr/fixed-in fixing NEWS entry
added:
tools/fixed-in.py fixedin.py-20100927214922-jxti5i4kt2ij9f6d-1
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2010-10-13 03:47:21 +0000
+++ b/NEWS 2010-10-13 06:33:55 +0000
@@ -59,6 +59,10 @@
Internals
*********
+* ``tools/fixed-in.py`` find a bug in NEWS from its number or a regexp
+ matching the news entry and display the corresponding release, date, fix
+ authors and the news entry itself. (Vincent Ladeuil)
+
Testing
*******
=== added file 'tools/fixed-in.py'
--- a/tools/fixed-in.py 1970-01-01 00:00:00 +0000
+++ b/tools/fixed-in.py 2010-09-28 08:57:31 +0000
@@ -0,0 +1,172 @@
+#!/usr/bin/python
+
+# Simple script that will output the release where a given bug was fixed
+# searching the NEWS file
+
+import optparse
+import re
+import sys
+
+
+class NewsParser(object):
+
+ paren_exp_re = re.compile('\(([^)]+)\)')
+ release_re = re.compile("bzr[ -]")
+ release_prefix_length = len('bzr ')
+ bugs_re = re.compile('#([0-9]+)')
+
+ def __init__(self, news):
+ self.news = news
+ # Temporary attributes used by the parser
+ self.release = None
+ self.date = None
+ self.may_be_release = None
+ self.release_markup = None
+ self.entry = ''
+ self.line = None
+ self.lrs = None
+
+ def set_line(self, line):
+ self.line = line
+ self.lrs = line.rstrip()
+
+ def try_release(self):
+ if self.release_re.match(self.lrs) is not None:
+ # May be a new release
+ self.may_be_release = self.lrs
+ # We know the markup will have the same length as the release
+ self.release_markup = '#' * len(self.may_be_release)
+ return True
+ return False
+
+ def confirm_release(self):
+ if self.may_be_release is not None and self.lrs == self.release_markup:
+ # The release is followed by the right markup
+ self.release = self.may_be_release[self.release_prefix_length:]
+ # Wait for the associated date
+ self.date = None
+ return True
+ return False
+
+ def try_date(self):
+ if self.release is None:
+ return False
+ date_re = re.compile(':%s: (NOT RELEASED YET|\d{4}-\d{2}-\d{2})'
+ % (self.release,))
+ match = date_re.match(self.lrs)
+ if match is not None:
+ self.date = match.group(1)
+ return True
+ # The old fashion way
+ released_re = re.compile(':Released:\s+(\d{4}-\d{2}-\d{2})')
+ match = released_re.match(self.lrs)
+ if match is not None:
+ self.date = match.group(1)
+ return True
+ return False
+
+ def add_line_to_entry(self):
+ if self.lrs == '':
+ return False
+ self.entry += self.line
+ return True
+
+ def extract_bugs_from_entry(self):
+ """Possibly extract bugs from a NEWS entry and yield them.
+
+ Not all entries will contain bugs and some entries are even garbage and
+ we don't try to parse them (yet). The trigger is a '#' and what looks
+ like a bug number inside parens to start with. From that we extract
+ authors (when present) and multiple bugs if needed.
+ """
+ # FIXME: Malone entries are different
+ # Join all entry lines to simplify multiple line matching
+ flat_entry = ' '.join(self.entry.splitlines())
+ # Fixed bugs are always inside parens
+ for par in self.paren_exp_re.findall(flat_entry):
+ sharp = par.find('#')
+ if sharp is not None:
+ # We have at least one bug inside parens.
+ bugs = list(self.bugs_re.finditer(par))
+ if bugs:
+ # See where the first bug is mentioned
+ start = bugs[0].start()
+ end = bugs[-1].end()
+ if start == 0:
+ # (bugs/authors)
+ authors = par[end:]
+ else:
+ # (authors/bugs)
+ authors = par[:start]
+ for bug_match in bugs:
+ bug_number = bug_match.group(0)
+ yield (bug_number, authors,
+ self.release, self.date, self.entry)
+ # We've consumed the entry
+ self.entry = ''
+
+ def parse_bugs(self):
+ for line in self.news:
+ self.set_line(line)
+ if self.try_release():
+ continue # line may a be release
+ try:
+ if self.confirm_release():
+ continue # previous line was indeed a release
+ finally:
+ self.may_be_release = None
+ if self.try_date():
+ continue # The release date has been seen
+ if self.add_line_to_entry():
+ continue # accumulate in self.enrty
+ for b in self.extract_bugs_from_entry():
+ yield b # all bugs in the news entry
+
+def main():
+ opt_parser = optparse.OptionParser(
+ usage="""Usage: %prog [options] BUG_NUMBER
+ """)
+ opt_parser.add_option(
+ '-f', '--file', type='str', dest='news_file',
+ help='NEWS file (defaults to ./NEWS)')
+ opt_parser.add_option(
+ '-m', '--message', type='str', dest='msg_re',
+ help='A regexp to search for in the news entry '
+ '(BUG_NUMBER should not be specified in this case)')
+ opt_parser.set_defaults(news_file='./NEWS')
+ (opts, args) = opt_parser.parse_args(sys.argv[1:])
+ if opts.msg_re is not None:
+ if len(args) != 0:
+ opt_parser.error('BUG_NUMBER and -m are mutually exclusive')
+ bug = None
+ msg_re = re.compile(opts.msg_re)
+ elif len(args) != 1:
+ opt_parser.error('Expected a single bug number, got %r' % args)
+ else:
+ bug = args[0]
+
+ news = open(opts.news_file)
+ parser = NewsParser(news)
+ try:
+ seen = 0
+ for b in parser.parse_bugs():
+ (number, authors, release, date, entry,) = b
+ # indent entry
+ entry = '\n'.join([' ' + l for l in entry.splitlines()])
+ found = False
+ if bug is not None:
+ if number[1:] == bug: # Strip the leading '#'
+ found = True
+ elif msg_re.search(entry) is not None:
+ found = True
+ if found:
+ print 'Bug %s was fixed in bzr-%s/%s by %s:' % (
+ number, release, date, authors)
+ print entry
+ seen += 1
+ finally:
+ print '%s bugs seen' % (seen,)
+ news.close()
+
+
+main()
More information about the bazaar-commits
mailing list