Ticket #2889: twisted_release_macro.py

File twisted_release_macro.py, 4.3 KB (added by thijs, 3 years ago)

ProjectVersion macro with md5 support

Line 
1"""
2Trac macros for Twisted.
3"""
4
5from StringIO import StringIO
6from urlparse import urlparse
7
8from trac.core import TracError
9from trac.util.html import Markup
10from trac.wiki.formatter import OneLinerFormatter
11from trac.wiki.macros import WikiMacroBase
12
13from twisted.python.versions import Version
14from twisted.python.filepath import FilePath
15
16
17RELEASES = FilePath('/srv/www-data/twisted/Releases/')
18
19
20
21class ProjectVersionMacro(WikiMacroBase):
22    """
23    Macro that knows the current [http://twistedmatrix.com Twisted] version number.
24
25    The version information is loaded from a folder containing text files with
26    md5sums for each released package/installer. Also see the
27    [http://twistedmatrix.com/trac/wiki/Downloads#SignedMD5Sums Twisted downloads]
28    page.
29
30    '''Standalone'''
31    {{{
32    [[ProjectVersion]]
33    }}}
34
35    produces:
36
37    [[ProjectVersion]]
38
39    '''URL'''
40
41    {{{
42    [[ProjectVersion(http://twistedmatrix.com/Releases/Twisted/%(major)s.%(minor)s/Twisted-%(base)s.win32-py2.7.msi Twisted %(base)s for Python 2.7)]]
43    }}}
44
45    produces:
46
47    [[ProjectVersion(http://twistedmatrix.com/Releases/Twisted/%(major)s.%(minor)s/Twisted-%(base)s.win32-py2.7.msi Twisted %(base)s for Python 2.7)]]
48
49    Including the MD5 hash (eg. `b568b504524fda2440c62aa1616b3fe5`):
50
51    {{{
52     - [[ProjectVersion(http://pypi.python.org/packages/source/T/Twisted/Twisted-%(base)s.tar.bz2#md5=%(md5)s Twisted %(base)s tar)]]
53     - [[ProjectVersion(http://pypi.python.org/packages/2.7/T/Twisted/Twisted-%(base)s.win32-py2.7.msi#md5=%(md5)s Twisted %(base)s for Python 2.7)]]
54    }}}
55
56    produces:
57
58     - [[ProjectVersion(http://pypi.python.org/packages/source/T/Twisted/Twisted-%(base)s.tar.bz2#md5=%(md5)s Twisted %(base)s tar)]]
59     - [[ProjectVersion(http://pypi.python.org/packages/2.7/T/Twisted/Twisted-%(base)s.win32-py2.7.msi#md5=%(md5)s Twisted %(base)s for Python 2.7)]]
60
61    '''Source browser'''
62
63    {{{
64    [[ProjectVersion(source:/tags/releases/twisted-%(base)s/ Tag for Twisted %(base)s)]]
65    }}}
66
67    produces:
68
69    [[ProjectVersion(source:/tags/releases/twisted-%(base)s/ Tag for Twisted %(base)s)]]
70    """
71
72
73    revision = "$Rev$"
74    url = "$URL$"
75
76    def getVersion(self):
77        versions = []
78        pattern = 'twisted-%s-md5sums.txt'
79        for md5sums in RELEASES.globChildren(pattern % '*'):
80            try:
81                components = map(int, md5sums.basename().split('-')[1].split('.'))
82            except ValueError:
83                pass
84            else:
85                versions.append(components)
86
87        version = Version('Twisted', *max(versions))
88        md5sums_file = RELEASES.child(pattern % version.base())
89        return version, md5sums_file
90
91
92    def expand_macro(self, formatter, name, args):
93        """
94        Return output that will be displayed in the Wiki content.
95
96        @param name: the actual name of the macro
97        @param args: the text enclosed in parenthesis at the call of the macro.
98          Note that if there are ''no'' parenthesis (like in, e.g.
99          [[ProjectVersion]]), then `args` is `None`.
100        """
101        if not RELEASES.exists():
102            self.log.error("The specified RELEASES directory does not exist at %s" % RELEASES.path)
103            raise TracError("Error loading Twisted version information")
104
105        v, md5sums = self.getVersion()       
106        md5sum = ''
107
108        if args is None:
109            text = v.base()
110        else:
111            uc = unicode(args).replace('%28', '(').replace('%29', ')')
112            if uc.find('%(md5)s') > -1:
113                sep = '-----BEGIN PGP SIGNATURE-----\n'
114                lines = md5sums.open().readlines()
115                path = urlparse(uc).path % dict(base=v.base())
116                filename = path.split('/')[-1]
117                for entry in lines[3:lines.index(sep)]:
118                    entry = entry.rstrip('\n').split('  ')
119                    if entry[1] == filename:
120                        md5sum = entry[0]
121                        break
122
123            url = urlparse(uc).netloc
124            text = uc % dict(major=v.major, minor=v.minor, micro=v.micro, base=v.base(),
125                             md5=md5sum)
126
127            # handle links
128            if args.startswith('source:') or url != '':
129                text = "[%s]" % text
130
131        out = StringIO()
132        OneLinerFormatter(self.env, formatter.context).format(text, out)
133
134        return Markup(out.getvalue())