aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2018-08-23 15:18:15 +0300
committerSergey Poznyakoff <gray@gnu.org>2018-08-23 15:18:15 +0300
commit1a9586f1498f89f48467bc4f58a75bd40cbce3bf (patch)
treedad19efbfad7b2b1a13fa64d4aca4197e40bc7b2
parent11688954d83f7bcd6952aff81f2b3203eaef4374 (diff)
downloadreleaselogparser-1a9586f1498f89f48467bc4f58a75bd40cbce3bf.tar.gz
releaselogparser-1a9586f1498f89f48467bc4f58a75bd40cbce3bf.tar.bz2
Document everything. Rename ReleaseLogBase to ReleaseHistory
-rw-r--r--releaselog.py33
-rw-r--r--releaselog/__init__.py122
-rw-r--r--releaselog/cpan.py15
-rw-r--r--releaselog/gnu.py16
4 files changed, 154 insertions, 32 deletions
diff --git a/releaselog.py b/releaselog.py
index a5a7c38..3109787 100644
--- a/releaselog.py
+++ b/releaselog.py
@@ -5,7 +5,14 @@ from __future__ import unicode_literals
import sys
from optparse import OptionParser
-from releaselog import ReleaseLog
+from releaselog.input import ReleaseLogFile, ReleaseLogURL
+
+# Set utf-8 as the default encoding for Python 2.7.
+try:
+ reload(sys)
+ sys.setdefaultencoding('utf-8')
+except:
+ pass
def main():
usage = '%prog [OPTIONS] ARG'
@@ -27,22 +34,22 @@ def main():
parser.add_option('-n', '--count',
action='store', type='int', dest='count',
help='read at most that much entries')
+ parser.add_option('-u', '--url',
+ action="store_true", dest='url',
+ help='treat ARG as URL')
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("bad number of arguments")
-
- with open(args[0], 'r') as infile:
- cl = ReleaseLog(options.logtype, infile,
- start=options.start,
- stop=options.stop,
- count=options.count)
- for r in cl:
- print(r)
- try:
- print(''.join(r.descr))
- except:
- pass
+
+ release_log = ReleaseLogURL if options.url else ReleaseLogFile
+ cl = release_log(options.logtype, args[0],
+ start=options.start,
+ stop=options.stop,
+ count=options.count)
+ for r in cl:
+ print(r)
+ print(''.join(r.descr))
if __name__ == '__main__':
main()
diff --git a/releaselog/__init__.py b/releaselog/__init__.py
index 0eeb4c7..be3f688 100644
--- a/releaselog/__init__.py
+++ b/releaselog/__init__.py
@@ -1,3 +1,14 @@
+"""
+ReleaseLog class
+
+Most software packages keep a history of last releases in some form. This
+module provides an abstraction for handling such information.
+
+Release history is represented by a ReleaseLog class. Each log contains a
+list of history entries in reverse chronological order.
+
+"""
+
from __future__ import print_function
from __future__ import unicode_literals
@@ -5,6 +16,15 @@ import re
import dateparser
class Release(object):
+ """Release - a single release history entry object
+
+ Attributes:
+
+ date -- datetime of the release
+ version -- release version number
+ descr -- textual description of the release
+
+ """
date = None
version = None
descr = None
@@ -16,19 +36,36 @@ class Release(object):
return "Version %s, released at %s" % (self.version, self.date)
-class ReleaseLogBase(object):
- releases = []
+class ReleaseHistory(object):
+ """ReleaseHistory - base class for ReleaseLog implementations
+
+ Attributes:
+
+ history - a list of Release objects
+ header - a compiled regular expression that returns a match for
+ history entry heading lines
+
+ """
+
+ history = []
def __len__(self):
- return len(self.releases)
+ return len(self.history)
+
+ def __getitem__(self, i):
+ return self.history[i]
def append(self, arg):
+ """Appends new release to the end of release history list"""
if isinstance(arg, Release):
- self.releases.append(arg)
+ self.history.append(arg)
else:
raise TypeError()
def parse_header(self, line):
+ """Matches line against the history header regexp. On match, returns
+ a tuple (date, version). On failure, returns (None, None).
+ """
date = None
version = None
m = self.header.match(line)
@@ -38,13 +75,38 @@ class ReleaseLogBase(object):
return date, version
def __init__(self, lines, **kwargs):
+ """Create a new history object from the list of lines. The list is
+ split into history entries by lines that match the header compiled
+ regexp. Matches should contain at least two groups:
+
+ version - part of line containing the release version
+ date - part of line containing the release date
+
+ The line is matching only if both groups are not None.
+
+ Additionally, a line starting with form-feed character (\f) or
+ containing a line of contiguous equals signs, optionally surrounded
+ by whitespace, is considered to terminate the current history entry.
+
+ Keyword arguments:
+
+ start=N
+ Start from the entry N
+ stop=N
+ Stop parsing on Nth entry
+ count=N
+ Collect at most N entries
+
+ If all three keywords are given, the actual range of history entries
+ is computed as
+
+ [start, min(start+count, stop)]
+
+ Entries are numbered from 0.
+ """
date = None
version = None
descr = []
- # args:
- # from=N
- # to=N
- # count=N
start = None
stop = None
@@ -102,18 +164,56 @@ class ReleaseLogBase(object):
self.append(Release(version, date, descr))
def __iter__(self):
- for r in self.releases:
+ for r in self.history:
yield(r)
class ReleaseLog(object):
+ """A release log class.
+
+ It is a fabric returning actual release history implementation, depending
+ on the first argument to constructor. Typical usage
+
+ cl = ReleaseLog('GNU', lines, count=1)
+
+ """
typedb = {}
def __new__(cls, type, *args, **kwargs):
+ """Object constructor:
+
+ ReleaseLog(type, lines, [start=N], [stop=N], [count=N]
+
+ Arguments:
+
+ type
+ Type of the history log. E.g. 'GNU' for GNU-style NEWS file, or
+ 'Changes', for CPAN-style Changes file.
+ lines
+ List of history lines.
+
+ Keyword arguments are the same as in ReleaseHistory.
+ """
+
return cls.typedb[type](*args, **kwargs)
@classmethod
- def deftype(cls, type, constr):
- cls.typedb[type] = constr.ReleaseLog
+ def deftype(cls, type, mod, name='ReleaseLog'):
+ """Register a new history implementation. Typical usage:
+
+ ReleaseLog.deftype(name, module)
+
+ Arguments:
+
+ name
+ Name of the implementation. It will subsequently be used as the
+ type argument to ReleaseLog constructor in order to require this
+ particular implementation.
+ module
+ Name of the module. The module should export the class ReleaseLog.
+ This requirement can be lifted by giving the class name as the
+ the third argument.
+ """
+ cls.typedb[type] = mod.__getattribute__(name)
# Initialize the ReleaseLog types
from releaselog import cpan, gnu
diff --git a/releaselog/cpan.py b/releaselog/cpan.py
index 95c8b74..a3087bb 100644
--- a/releaselog/cpan.py
+++ b/releaselog/cpan.py
@@ -1,11 +1,18 @@
# -*- coding: utf-8 -*-
-from __future__ import print_function
-from __future__ import unicode_literals
+"""
+Implementation of the CPAN 'Changes' history format.
+Usage:
+
+ from releaselog import ReleaseLog, cpan
+ ReleaseLog.deftype('Changes', cpan)
+
+This is normally done as a part of initialization of the releaselog module.
+"""
import re
-from releaselog import ReleaseLogBase
+from releaselog import ReleaseHistory
-class ReleaseLog(ReleaseLogBase):
+class ReleaseLog(ReleaseHistory):
header = re.compile('^(?P<version>\d[\d.]*)\s+(?P<date>.+?)\s*$')
diff --git a/releaselog/gnu.py b/releaselog/gnu.py
index ed9e82e..1730d1f 100644
--- a/releaselog/gnu.py
+++ b/releaselog/gnu.py
@@ -1,11 +1,19 @@
# -*- coding: utf-8 -*-
-from __future__ import print_function
-from __future__ import unicode_literals
+"""
+Implementation of the GNU-style 'NEWS' history format.
+
+Usage:
+
+ from releaselog import ReleaseLog, gnu
+ ReleaseLog.deftype('NEWS', gnu)
+
+This is normally done as a part of initialization of the releaselog module.
+"""
import re
-from releaselog import ReleaseLogBase
+from releaselog import ReleaseHistory
-class ReleaseLog(ReleaseLogBase):
+class ReleaseLog(ReleaseHistory):
header = re.compile(r"""^(?:\*\s+)? # optional initial section
(?:(?i)version)\s+
(?P<version>\d(?:[.,]\d+){1,2} # At least MAJOR.MINOR

Return to:

Send suggestions and report system problems to the System administrator.