1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4
<title>Managing the Release of a Large Python Project</title>
8
<h1>Managing the Release of a Large Python Project</h1>
11
<li>Christopher Armstrong <a href="mailto:radix@twistedmatrix.com">radix@twistedmatrix.com</a></li>
12
<li>Moshe Zadka <a href="mailto:moshez@twistedmatrix.com">moshez@twistedmatrix.com</a></li>
18
Twisted is a Python networking framework. At last count, the project
19
contains nearly 60,000 lines of effective code (not comments or blank
20
lines). When preparing a release, many details must be checked, and
21
many steps must be followed. We describe here the technologies and
22
tools we use, and explain how we built tools on top of them which help
23
us make releasing as painless as possible.
30
One of the virtues of Python is the ease of distributing code. Its
31
module system and the lack of necessity of compilation are what make
32
this possible. This means that for simple Python projects, nothing
33
more complicated then tar is needed to prepare a distribution of a
34
library. However, Twisted has auto-generated documentation in several
35
formats, including docstring generated documentation, HOWTOs written
36
in HTML, and manpages written in nroff. As Twisted grew more complex
37
and popular, a detailed procedure for putting out a release was made
38
necessary. However, human fallibility being what it is, it was decided
39
that most of these steps should be automated.
43
<h2>Overview of Steps</h2>
46
Despite heavy automation, there are still a number of manual steps
47
involved in the release process. We've reduced the amount of manual
48
steps quite a bit, and most of what's left is not fully automatable,
49
although the process could be made easier (see <q>Future
50
Directions</q> below).
58
<li>Acceptance tests</li>
59
<li>Pre-release tests</li>
62
<li>Update the Changelog and README files</li>
63
<li>Run the release script
65
<li>unix runs admin/release-twisted</li>
66
<li>Win32 runs win32/bdist_wininst.bat</li>
69
<li>Deploy: update twisted deployment on twistedmatrix.com</li>
70
<li>Upload to SourceForge mirror</li>
71
<li>Update Website</li>
80
Twisted has three categories of tests: unit, acceptance, and
81
pre-release. Testing is an important part of releasing quality
82
software, of course, so these will be explained.
89
Unit tests are run as often as possible by each of the developers as
90
they write code, and must pass before they commit any changes to
91
CVS. While the Twisted team tries to follow the XP practice of
92
ensuring all code is releasable, this isn't always true. Thus, running
93
the unit tests on several platforms before releasing is necessary.
94
Our BuildBot runs the unit tests constantly on several hosts and
95
multiple platforms, so the <a
96
href="http://twistedmatrix.com/users/warner.twistd/">status page</a>
97
is simply checked for green lights before a release.
103
Acceptance tests (which, unfortunately, are not quite the same as <a
104
href="http://xprogramming.org/">Extreme Programming's</a> Acceptance
105
Tests) are simply interactive tests of various Twisted services. There
106
is a script that executes several system commands that use the Twisted
107
end-user executables and start several clients (web browsers, IRC
108
clients, etc) to allow the user to interactively test the different
109
services that Twisted offers. These are only routinely run before a
110
release, but we also encourage developers to run these before they
117
The pre-release tests are for ensuring the web server (One of the most
118
popular parts of Twisted, and which the twistedmatrix.com web site
119
uses) runs correctly in a semi-production environment. The script
120
starts up a web server on twistedmatrix.com, similar to the one on
121
port 80, but on an out-of-the-way port. <q>lynx</q> is then run
122
several times, with URLs strategically chosen to test different
123
features of the web server. Afterwards, the log of the web server is
124
displayed and the user is to check for any errors.
129
<h2>The release-twisted Script</h2>
133
Like many other build/release systems, the automated parts of our
134
release system started out as a number of small shell
135
scripts. Eventually these became a single Python script which was a
136
large improvement, but still had many problems, especially since our
137
release process became more complex (documentation generation,
138
different types of archive formats, etc). This led to problems with
139
steps in the middle of the process breaking; the release manager would
140
need to restart the entire thing, or enter the remaining commands
147
The solution that we came up with was a simple framework for
148
pseudo-transactions; Every step of the process is implemented with a
149
class that has <code class="python">doIt</code> and <code
150
class="python">undoIt</code> methods. Each step also has a
151
command-line argument associated with it, so a typical run of the
152
script looks something like this:
155
$SOMEWHERE/admin/release-twisted -V $VERSION -o $LASTVERSION --checkout \
156
--release=/twisted/Releases --upver --tag --exp --dist --docs --balls \
162
<h3>Transactions</h3>
166
As stated above, our transaction system is very simple. One of our
167
rather simple transaction classes is <code
168
class="python">Export</code>.
174
class Export(Transaction):
175
def doIt(self, opts):
177
root = opts['cvsroot']
178
ver = opts['release-version']
179
sh('cvs -d%s export -r release-%s Twisted' % (root, ver.replace('.', '_')))
181
def undoIt(self, opts, fail):
188
One useful feature to note is the <code
189
class="python">sensitiveUndo</code> attribute on Transaction
190
classes. If a transaction has this set, the user will be prompted
191
before running the <code class="python">undoIt</code> method. This is
192
useful for very long-running processes, like documentation generation,
193
debian package building, and uploading to sourceforge. If something
194
goes wrong in the middle of one of these processes, we want to give
195
the user a chance to manually fix the problem rather than redoing the
196
entire transaction. They can then continue from the next command by
197
omitting the commands that have already been accomplished from the
198
<code class="shell">release-twisted</code> arguments.
204
A list of all of the transactions defined in release-twisted follows.
212
checks out the latest revision of Twisted from CVS and puts it in
213
the <q>Twisted.CVS</q> directory.
217
<dt>UpdateVersion</dt>
220
changes the version number of the current release -- updating
221
twisted/copyright.py (the canonical location for the current
222
version) and a few other text files where the current version is
231
tags the revisions in the current source tree with the version
232
passed in on the command line.
241
runs the cvs <q>export</q> command, which is similar to
242
<q>checkout</q>, but leaves out CVS support directories; this is
243
what we package up in the archives.
251
simply copies the directory containing the version of Twisted to be
252
released to a new directory specifically for the release
253
process. The reason that we have this extra copy is that sometimes
254
one will want to create a release from a directory that wasn't
255
created from the <q>Export</q> command; having the release script
256
munge that directory in-place would be impolite.
261
<dt>GenerateDocs</dt>
265
generates the various documentation: HTML API documentation (via
266
Epydoc), HTML, PostScript, and PDF howto documentation (via
267
twisted.lore), and HTML man-pages (via lore, converted from the
272
<dt>CreateTarballs</dt>
275
creates the various archives that each Twisted release involves:
276
tarred and gzipped or bzip2ed versions of archives with code plus
277
documentation, code without documentation, and only documentation.
286
copies all of the archives to a directory specified by the --release
287
parameter. This is meant to be a publically accessible directory,
288
thus the name <q>Release</q>.
296
creates the .deb packages and support files for the Twisted Debian
305
Creates an apt-gettable Debian package repository in the
306
(unfortunately hard-coded) <q>/twisted/Debian</q> directory.
314
uploads the archives and debian packages to Twisted's sourceforge
316
href="http://twisted.sourceforge.net">http://twisted.sourceforge.net/</a>.
321
<dt>UpgradeDebian</dt>
325
Installs the recently-generated Debian packages via <q>dpkg</q> on
337
Twisted has an extensive and very customized setup.py script. We have
338
a number of C extension modules and try to ensure that they all build,
339
or at least fail gracefully, on win32, Mac OSX, Linux and other
340
popular unix-style OSes.
346
We have overridden three of the distutils <q>command classes</q>:
347
<code class="python">build_ext</code>, <code
348
class="python">install_scripts</code>, and <code
349
class="python">install_data</code>.
354
<h3>Building C extensions</h3>
358
<code class="python">build_ext_twisted</code> detects, based on
359
various features of the platform, which C extensions to build. It
360
overrides the <code class="python">build_extensions</code> method to
361
first check which C extensions are appropriate to build for the
362
current platform before proceeding as normal (by calling the
363
superclass's <code class="python">build_extensions</code>). The
364
module-detection consists of several simple tests for platform
365
features and conditional additions to the `extensions' attribute. One
366
especially useful feature is the <code
367
class="python">_check_header</code> method, which takes the name of an
368
arbitrary head file and tries to compile (via the distutil's C
369
compiler interafce) a simple C file that only #includes it.
374
<h3>Installing scripts</h3>
379
<code class="python">install_data_twisted</code> ensures that the data
380
files are installed along-side the python modules in the twisted
381
package. This is accomplished with the incantation:
386
class install_data_twisted(install_data):
387
def finalize_options (self):
388
self.set_undefined_options('install',
389
('install_lib', 'install_dir')
391
install_data.finalize_options(self)
396
<h3>Windows Releases</h3>
400
This section will cover the problems with packaging Python projects
401
for windows, especially ones which contain scripts. The problem of
402
clickability is especially acute, as windows determines types by
403
extensions and not by #! lines.
409
Packaging software for windows involves a unique set of problems. The
410
problem of clickability is especially acute; Several customizations to
411
the distutils setup had to be made.
417
The first customization was to make the <q>scripts</q> end with a
418
<q>.py</q> extension, since Windows relies on extension rather than a
419
she-bang line to specify what interpreter should execute a file. This
420
was accomplished by overriding the <code
421
class="python">install_scripts</code> command, like so:
426
class install_scripts_twisted(install_scripts):
427
"""Renames scripts so they end with '.py' on Windows."""
430
install_scripts.run(self)
432
for file in self.get_outputs():
433
if not file.endswith(".py"):
434
os.rename(file, file + ".py")
440
We also wanted to have a Start-menu group with a number of icons for
441
running different Twisted programs. This was accomplished with a
442
post-install script specified with the command-line parameter
443
<code class="shell">--install-script=twisted_postinstall.py</code>.
449
<h2>Future Directions</h2>
453
The theme is, of course, automation, and there are still many manual
454
steps involved in a Twisted release. The currently most annoying step
455
is updating the documentation and downloads section of the
456
twistedmatrix.com website. Automating this would be a major
457
improvement to the time it takes from the running of the release
458
script to a fully completed release.
464
Another major improvement will involve further integration with
465
BuildBot. Currently we have BuildBot running unit tests, building C
466
extensions, and generating documentation on several hosts. Eventually
467
we would like to have it constantly generating full release archives,
468
and have an additional web form for <q>finalizing</q> any particular
469
build that we deem releasable. The result would be uploading the
470
release to the mirrors and updating the website.
476
The tagging scheme used by the release-twisted scripts can sometimes
477
be problematic. If we find serious problems in the code-base after the
478
Tag command is executed (which is fairly early in the process), we are
479
forced to fix the bug and increase the version number. This can be
480
prevented by, instead of making the official tag, using the unofficial
481
tag <q>releasing-$version</q> (as opposed to <q>release-$version</q>)
482
at that early stage. Once most of the steps are complete, the official
483
tag will be made. If something in between goes wrong, we can just
484
re-use the unofficial <q>releasing-$version</q> tag and not worry
485
about users trying to use that tag.