aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-08-24 22:24:07 +0300
committerSergey Poznyakoff <gray@gnu.org>2018-08-24 22:32:47 +0300
commit5b49acb829c3fd077f7946ef8a3ba6ee3d6de888 (patch)
tree578ad2dd0be9b0aa952ba43a02e0ad1d25359cd6
parentb14aa091903560d2f156e6f3549d373c65fe3b2d (diff)
downloadreleaselogparser-5b49acb829c3fd077f7946ef8a3ba6ee3d6de888.tar.gz
releaselogparser-5b49acb829c3fd077f7946ef8a3ba6ee3d6de888.tar.bz2
Implement Python-style release log format
* releaselog.py (main): Explicitly add newline characters. * releaselog/__init__.py (end_of_entry_rx): New attribute. (parse_header): Return a triple: (date, version, line), where line is first line of the description or None. (is_end_of_entry): New method. (__init__): Strip trailing newline from the description lines. * releaselog/format/python.py: New file.
-rw-r--r--releaselog.py2
-rw-r--r--releaselog/__init__.py32
-rw-r--r--releaselog/format/python.py54
3 files changed, 81 insertions, 7 deletions
diff --git a/releaselog.py b/releaselog.py
index 3109787..c8eeff3 100644
--- a/releaselog.py
+++ b/releaselog.py
@@ -49,7 +49,7 @@ def main():
49 count=options.count) 49 count=options.count)
50 for r in cl: 50 for r in cl:
51 print(r) 51 print(r)
52 print(''.join(r.descr)) 52 print('\n'.join(r.descr))
53 53
54if __name__ == '__main__': 54if __name__ == '__main__':
55 main() 55 main()
diff --git a/releaselog/__init__.py b/releaselog/__init__.py
index dfee183..7cb9c7d 100644
--- a/releaselog/__init__.py
+++ b/releaselog/__init__.py
@@ -44,9 +44,13 @@ class ReleaseHistory(object):
44 history - a list of Release objects 44 history - a list of Release objects
45 header - a compiled regular expression that returns a match for 45 header - a compiled regular expression that returns a match for
46 history entry heading lines 46 history entry heading lines
47 end_of_entry_rx - a compiled regular expression returning a match for
48 end of entry. Can be None
47 """ 49 """
48 50
49 history = [] 51 history = []
52
53 end_of_entry_rx = re.compile('^(\f|^\s*=+\s*$)')
50 54
51 def __len__(self): 55 def __len__(self):
52 return len(self.history) 56 return len(self.history)
@@ -62,16 +66,30 @@ class ReleaseHistory(object):
62 raise TypeError() 66 raise TypeError()
63 67
64 def parse_header(self, line): 68 def parse_header(self, line):
65 """Matches line against the history header regexp. On match, returns 69 """Matche input line against the history header regexp. On match,
66 a tuple (date, version). On failure, returns (None, None). 70 return a tuple (date, version, startdescr), where date is the
71 release date (datetime), version is the release version number, and
72 startdescr is the first line of the description or None.
73 On failure, return (None, None, None).
67 """ 74 """
68 date = None 75 date = None
69 version = None 76 version = None
77 rest = None
70 m = self.header.match(line) 78 m = self.header.match(line)
71 if m: 79 if m:
72 version = m.group('version') 80 version = m.group('version')
73 date = dateparser.parse(m.group('date')) 81 date = dateparser.parse(m.group('date'))
74 return date, version 82 try:
83 rest = m.group('rest')
84 if len(rest) == 0:
85 rest = None
86 except IndexError:
87 pass
88 return date, version, rest
89
90 def is_end_of_entry(self, line):
91 return (self.end_of_entry_rx.match(line)
92 if self.end_of_entry_rx else False)
75 93
76 def __init__(self, lines, **kwargs): 94 def __init__(self, lines, **kwargs):
77 """Create a new history object from the list of lines. The list is 95 """Create a new history object from the list of lines. The list is
@@ -134,7 +152,7 @@ class ReleaseHistory(object):
134 152
135 i = 0 153 i = 0
136 for line in lines: 154 for line in lines:
137 (d, v) = self.parse_header(line) 155 (d, v, r) = self.parse_header(line)
138 if d: 156 if d:
139 if date: 157 if date:
140 self.append(Release(version, date, descr)) 158 self.append(Release(version, date, descr))
@@ -151,14 +169,16 @@ class ReleaseHistory(object):
151 date = d 169 date = d
152 version = v 170 version = v
153 descr = [] 171 descr = []
154 elif re.match('^(\f|^\s*=+\s*$)', line): 172 if r:
173 descr.append(r)
174 elif self.is_end_of_entry(line):
155 if date: 175 if date:
156 self.append(Release(version, date, descr)) 176 self.append(Release(version, date, descr))
157 date = None 177 date = None
158 version = None 178 version = None
159 descr = [] 179 descr = []
160 elif date: 180 elif date:
161 descr.append(line) 181 descr.append(line.rstrip("\n"))
162 if date: 182 if date:
163 self.append(Release(version, date, descr)) 183 self.append(Release(version, date, descr))
164 184
diff --git a/releaselog/format/python.py b/releaselog/format/python.py
new file mode 100644
index 0000000..7c2a218
--- /dev/null
+++ b/releaselog/format/python.py
@@ -0,0 +1,54 @@
1# -*- coding: utf-8 -*-
2"""
3Implementation of two release log formats used in most Python packages.
4
5The two formats are:
6
7* Each entry begins with the line
8
9 v<Version>, <Date> -- <String>
10
11where <String> is the beginning of the description. More description lines
12may follow.
13
14* Each entry begins with the line
15
16 <Version> (<Date>)
17
18followed by the description text. This format is used, among others, by
19GNU Texinfo.
20
21The PythonLogFormat class discovers the actual format by finding the first
22input line that matches any of the above patterns.
23
24"""
25
26import re
27from releaselog import ReleaseHistory
28
29class PythonLogFormat(ReleaseHistory):
30 format = ['Python', 'python']
31 header = None
32 header_rx = [
33 re.compile("""^[vV](?P<version>\d[\d.]*)\s*
34 ,\s*
35 (?P<date>.*?)
36 \s+-+\s*
37 (?P<rest>.*)$
38 """, re.X),
39 re.compile("""^(?P<version>\d[\d.]*)
40 \s*
41 (?P<date>.*)
42 """, re.X)
43 ]
44
45 def parse_header(self, line):
46 if self.header:
47 return super(PythonLogFormat, self).parse_header(line)
48 else:
49 for rx in self.header_rx:
50 if rx.match(line):
51 self.header = rx
52 return super(PythonLogFormat, self).parse_header(line)
53 return (None, None, None)
54

Return to:

Send suggestions and report system problems to the System administrator.