Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(105)

Side by Side Diff: third_party/logilab/common/changelog.py

Issue 10447014: Add pylint to depot_tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Fix unittests. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/logilab/common/cache.py ('k') | third_party/logilab/common/clcommands.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of logilab-common.
5 #
6 # logilab-common is free software: you can redistribute it and/or modify it unde r
7 # the terms of the GNU Lesser General Public License as published by the Free
8 # Software Foundation, either version 2.1 of the License, or (at your option) an y
9 # later version.
10 #
11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License along
17 # with logilab-common. If not, see <http://www.gnu.org/licenses/>.
18 """Manipulation of upstream change log files.
19
20 The upstream change log files format handled is simpler than the one
21 often used such as those generated by the default Emacs changelog mode.
22
23 Sample ChangeLog format::
24
25 Change log for project Yoo
26 ==========================
27
28 --
29 * add a new functionality
30
31 2002-02-01 -- 0.1.1
32 * fix bug #435454
33 * fix bug #434356
34
35 2002-01-01 -- 0.1
36 * initial release
37
38
39 There is 3 entries in this change log, one for each released version and one
40 for the next version (i.e. the current entry).
41 Each entry contains a set of messages corresponding to changes done in this
42 release.
43 All the non empty lines before the first entry are considered as the change
44 log title.
45 """
46
47 __docformat__ = "restructuredtext en"
48
49 import sys
50 from stat import S_IWRITE
51
52 BULLET = '*'
53 SUBBULLET = '-'
54 INDENT = ' ' * 4
55
56 class NoEntry(Exception):
57 """raised when we are unable to find an entry"""
58
59 class EntryNotFound(Exception):
60 """raised when we are unable to find a given entry"""
61
62 class Version(tuple):
63 """simple class to handle soft version number has a tuple while
64 correctly printing it as X.Y.Z
65 """
66 def __new__(cls, versionstr):
67 if isinstance(versionstr, basestring):
68 versionstr = versionstr.strip(' :') # XXX (syt) duh?
69 parsed = cls.parse(versionstr)
70 else:
71 parsed = versionstr
72 return tuple.__new__(cls, parsed)
73
74 @classmethod
75 def parse(cls, versionstr):
76 versionstr = versionstr.strip(' :')
77 try:
78 return [int(i) for i in versionstr.split('.')]
79 except ValueError, ex:
80 raise ValueError("invalid literal for version '%s' (%s)"%(versionstr , ex))
81
82 def __str__(self):
83 return '.'.join([str(i) for i in self])
84
85 # upstream change log #########################################################
86
87 class ChangeLogEntry(object):
88 """a change log entry, i.e. a set of messages associated to a version and
89 its release date
90 """
91 version_class = Version
92
93 def __init__(self, date=None, version=None, **kwargs):
94 self.__dict__.update(kwargs)
95 if version:
96 self.version = self.version_class(version)
97 else:
98 self.version = None
99 self.date = date
100 self.messages = []
101
102 def add_message(self, msg):
103 """add a new message"""
104 self.messages.append(([msg], []))
105
106 def complete_latest_message(self, msg_suite):
107 """complete the latest added message
108 """
109 if not self.messages:
110 raise ValueError('unable to complete last message as there is no pre vious message)')
111 if self.messages[-1][1]: # sub messages
112 self.messages[-1][1][-1].append(msg_suite)
113 else: # message
114 self.messages[-1][0].append(msg_suite)
115
116 def add_sub_message(self, sub_msg, key=None):
117 if not self.messages:
118 raise ValueError('unable to complete last message as there is no pre vious message)')
119 if key is None:
120 self.messages[-1][1].append([sub_msg])
121 else:
122 raise NotImplementedError("sub message to specific key are not imple mented yet")
123
124 def write(self, stream=sys.stdout):
125 """write the entry to file """
126 stream.write('%s -- %s\n' % (self.date or '', self.version or ''))
127 for msg, sub_msgs in self.messages:
128 stream.write('%s%s %s\n' % (INDENT, BULLET, msg[0]))
129 stream.write(''.join(msg[1:]))
130 if sub_msgs:
131 stream.write('\n')
132 for sub_msg in sub_msgs:
133 stream.write('%s%s %s\n' % (INDENT * 2, SUBBULLET, sub_msg[0]))
134 stream.write(''.join(sub_msg[1:]))
135 stream.write('\n')
136
137 stream.write('\n\n')
138
139 class ChangeLog(object):
140 """object representation of a whole ChangeLog file"""
141
142 entry_class = ChangeLogEntry
143
144 def __init__(self, changelog_file, title=''):
145 self.file = changelog_file
146 self.title = title
147 self.additional_content = ''
148 self.entries = []
149 self.load()
150
151 def __repr__(self):
152 return '<ChangeLog %s at %s (%s entries)>' % (self.file, id(self),
153 len(self.entries))
154
155 def add_entry(self, entry):
156 """add a new entry to the change log"""
157 self.entries.append(entry)
158
159 def get_entry(self, version='', create=None):
160 """ return a given changelog entry
161 if version is omitted, return the current entry
162 """
163 if not self.entries:
164 if version or not create:
165 raise NoEntry()
166 self.entries.append(self.entry_class())
167 if not version:
168 if self.entries[0].version and create is not None:
169 self.entries.insert(0, self.entry_class())
170 return self.entries[0]
171 version = self.version_class(version)
172 for entry in self.entries:
173 if entry.version == version:
174 return entry
175 raise EntryNotFound()
176
177 def add(self, msg, create=None):
178 """add a new message to the latest opened entry"""
179 entry = self.get_entry(create=create)
180 entry.add_message(msg)
181
182 def load(self):
183 """ read a logilab's ChangeLog from file """
184 try:
185 stream = open(self.file)
186 except IOError:
187 return
188 last = None
189 expect_sub = False
190 for line in stream.readlines():
191 sline = line.strip()
192 words = sline.split()
193 # if new entry
194 if len(words) == 1 and words[0] == '--':
195 expect_sub = False
196 last = self.entry_class()
197 self.add_entry(last)
198 # if old entry
199 elif len(words) == 3 and words[1] == '--':
200 expect_sub = False
201 last = self.entry_class(words[0], words[2])
202 self.add_entry(last)
203 # if title
204 elif sline and last is None:
205 self.title = '%s%s' % (self.title, line)
206 # if new entry
207 elif sline and sline[0] == BULLET:
208 expect_sub = False
209 last.add_message(sline[1:].strip())
210 # if new sub_entry
211 elif expect_sub and sline and sline[0] == SUBBULLET:
212 last.add_sub_message(sline[1:].strip())
213 # if new line for current entry
214 elif sline and last.messages:
215 last.complete_latest_message(line)
216 else:
217 expect_sub = True
218 self.additional_content += line
219 stream.close()
220
221 def format_title(self):
222 return '%s\n\n' % self.title.strip()
223
224 def save(self):
225 """write back change log"""
226 # filetutils isn't importable in appengine, so import locally
227 from logilab.common.fileutils import ensure_fs_mode
228 ensure_fs_mode(self.file, S_IWRITE)
229 self.write(open(self.file, 'w'))
230
231 def write(self, stream=sys.stdout):
232 """write changelog to stream"""
233 stream.write(self.format_title())
234 for entry in self.entries:
235 entry.write(stream)
236
OLDNEW
« no previous file with comments | « third_party/logilab/common/cache.py ('k') | third_party/logilab/common/clcommands.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698