Adding new stuff
This commit is contained in:
parent
39ee792ad4
commit
a410da0e04
722 changed files with 331 additions and 189 deletions
|
|
@ -1,238 +0,0 @@
|
|||
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
|
||||
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
||||
#
|
||||
# This file is part of logilab-common.
|
||||
#
|
||||
# logilab-common is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Lesser General Public License as published by the Free
|
||||
# Software Foundation, either version 2.1 of the License, or (at your option) any
|
||||
# later version.
|
||||
#
|
||||
# logilab-common is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License along
|
||||
# with logilab-common. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""Manipulation of upstream change log files.
|
||||
|
||||
The upstream change log files format handled is simpler than the one
|
||||
often used such as those generated by the default Emacs changelog mode.
|
||||
|
||||
Sample ChangeLog format::
|
||||
|
||||
Change log for project Yoo
|
||||
==========================
|
||||
|
||||
--
|
||||
* add a new functionality
|
||||
|
||||
2002-02-01 -- 0.1.1
|
||||
* fix bug #435454
|
||||
* fix bug #434356
|
||||
|
||||
2002-01-01 -- 0.1
|
||||
* initial release
|
||||
|
||||
|
||||
There is 3 entries in this change log, one for each released version and one
|
||||
for the next version (i.e. the current entry).
|
||||
Each entry contains a set of messages corresponding to changes done in this
|
||||
release.
|
||||
All the non empty lines before the first entry are considered as the change
|
||||
log title.
|
||||
"""
|
||||
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
import sys
|
||||
from stat import S_IWRITE
|
||||
|
||||
from six import string_types
|
||||
|
||||
BULLET = '*'
|
||||
SUBBULLET = '-'
|
||||
INDENT = ' ' * 4
|
||||
|
||||
class NoEntry(Exception):
|
||||
"""raised when we are unable to find an entry"""
|
||||
|
||||
class EntryNotFound(Exception):
|
||||
"""raised when we are unable to find a given entry"""
|
||||
|
||||
class Version(tuple):
|
||||
"""simple class to handle soft version number has a tuple while
|
||||
correctly printing it as X.Y.Z
|
||||
"""
|
||||
def __new__(cls, versionstr):
|
||||
if isinstance(versionstr, string_types):
|
||||
versionstr = versionstr.strip(' :') # XXX (syt) duh?
|
||||
parsed = cls.parse(versionstr)
|
||||
else:
|
||||
parsed = versionstr
|
||||
return tuple.__new__(cls, parsed)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, versionstr):
|
||||
versionstr = versionstr.strip(' :')
|
||||
try:
|
||||
return [int(i) for i in versionstr.split('.')]
|
||||
except ValueError as ex:
|
||||
raise ValueError("invalid literal for version '%s' (%s)"%(versionstr, ex))
|
||||
|
||||
def __str__(self):
|
||||
return '.'.join([str(i) for i in self])
|
||||
|
||||
# upstream change log #########################################################
|
||||
|
||||
class ChangeLogEntry(object):
|
||||
"""a change log entry, i.e. a set of messages associated to a version and
|
||||
its release date
|
||||
"""
|
||||
version_class = Version
|
||||
|
||||
def __init__(self, date=None, version=None, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
if version:
|
||||
self.version = self.version_class(version)
|
||||
else:
|
||||
self.version = None
|
||||
self.date = date
|
||||
self.messages = []
|
||||
|
||||
def add_message(self, msg):
|
||||
"""add a new message"""
|
||||
self.messages.append(([msg], []))
|
||||
|
||||
def complete_latest_message(self, msg_suite):
|
||||
"""complete the latest added message
|
||||
"""
|
||||
if not self.messages:
|
||||
raise ValueError('unable to complete last message as there is no previous message)')
|
||||
if self.messages[-1][1]: # sub messages
|
||||
self.messages[-1][1][-1].append(msg_suite)
|
||||
else: # message
|
||||
self.messages[-1][0].append(msg_suite)
|
||||
|
||||
def add_sub_message(self, sub_msg, key=None):
|
||||
if not self.messages:
|
||||
raise ValueError('unable to complete last message as there is no previous message)')
|
||||
if key is None:
|
||||
self.messages[-1][1].append([sub_msg])
|
||||
else:
|
||||
raise NotImplementedError("sub message to specific key are not implemented yet")
|
||||
|
||||
def write(self, stream=sys.stdout):
|
||||
"""write the entry to file """
|
||||
stream.write('%s -- %s\n' % (self.date or '', self.version or ''))
|
||||
for msg, sub_msgs in self.messages:
|
||||
stream.write('%s%s %s\n' % (INDENT, BULLET, msg[0]))
|
||||
stream.write(''.join(msg[1:]))
|
||||
if sub_msgs:
|
||||
stream.write('\n')
|
||||
for sub_msg in sub_msgs:
|
||||
stream.write('%s%s %s\n' % (INDENT * 2, SUBBULLET, sub_msg[0]))
|
||||
stream.write(''.join(sub_msg[1:]))
|
||||
stream.write('\n')
|
||||
|
||||
stream.write('\n\n')
|
||||
|
||||
class ChangeLog(object):
|
||||
"""object representation of a whole ChangeLog file"""
|
||||
|
||||
entry_class = ChangeLogEntry
|
||||
|
||||
def __init__(self, changelog_file, title=''):
|
||||
self.file = changelog_file
|
||||
self.title = title
|
||||
self.additional_content = ''
|
||||
self.entries = []
|
||||
self.load()
|
||||
|
||||
def __repr__(self):
|
||||
return '<ChangeLog %s at %s (%s entries)>' % (self.file, id(self),
|
||||
len(self.entries))
|
||||
|
||||
def add_entry(self, entry):
|
||||
"""add a new entry to the change log"""
|
||||
self.entries.append(entry)
|
||||
|
||||
def get_entry(self, version='', create=None):
|
||||
""" return a given changelog entry
|
||||
if version is omitted, return the current entry
|
||||
"""
|
||||
if not self.entries:
|
||||
if version or not create:
|
||||
raise NoEntry()
|
||||
self.entries.append(self.entry_class())
|
||||
if not version:
|
||||
if self.entries[0].version and create is not None:
|
||||
self.entries.insert(0, self.entry_class())
|
||||
return self.entries[0]
|
||||
version = self.version_class(version)
|
||||
for entry in self.entries:
|
||||
if entry.version == version:
|
||||
return entry
|
||||
raise EntryNotFound()
|
||||
|
||||
def add(self, msg, create=None):
|
||||
"""add a new message to the latest opened entry"""
|
||||
entry = self.get_entry(create=create)
|
||||
entry.add_message(msg)
|
||||
|
||||
def load(self):
|
||||
""" read a logilab's ChangeLog from file """
|
||||
try:
|
||||
stream = open(self.file)
|
||||
except IOError:
|
||||
return
|
||||
last = None
|
||||
expect_sub = False
|
||||
for line in stream.readlines():
|
||||
sline = line.strip()
|
||||
words = sline.split()
|
||||
# if new entry
|
||||
if len(words) == 1 and words[0] == '--':
|
||||
expect_sub = False
|
||||
last = self.entry_class()
|
||||
self.add_entry(last)
|
||||
# if old entry
|
||||
elif len(words) == 3 and words[1] == '--':
|
||||
expect_sub = False
|
||||
last = self.entry_class(words[0], words[2])
|
||||
self.add_entry(last)
|
||||
# if title
|
||||
elif sline and last is None:
|
||||
self.title = '%s%s' % (self.title, line)
|
||||
# if new entry
|
||||
elif sline and sline[0] == BULLET:
|
||||
expect_sub = False
|
||||
last.add_message(sline[1:].strip())
|
||||
# if new sub_entry
|
||||
elif expect_sub and sline and sline[0] == SUBBULLET:
|
||||
last.add_sub_message(sline[1:].strip())
|
||||
# if new line for current entry
|
||||
elif sline and last.messages:
|
||||
last.complete_latest_message(line)
|
||||
else:
|
||||
expect_sub = True
|
||||
self.additional_content += line
|
||||
stream.close()
|
||||
|
||||
def format_title(self):
|
||||
return '%s\n\n' % self.title.strip()
|
||||
|
||||
def save(self):
|
||||
"""write back change log"""
|
||||
# filetutils isn't importable in appengine, so import locally
|
||||
from logilab.common.fileutils import ensure_fs_mode
|
||||
ensure_fs_mode(self.file, S_IWRITE)
|
||||
self.write(open(self.file, 'w'))
|
||||
|
||||
def write(self, stream=sys.stdout):
|
||||
"""write changelog to stream"""
|
||||
stream.write(self.format_title())
|
||||
for entry in self.entries:
|
||||
entry.write(stream)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue