The single exception in naming of functions and methods. Because Buildbot uses
Twisted so heavily, and Twisted uses interCaps, Buildbot methods should do the
same. That is, you should spell methods and functions with the first character
in lower-case, and the first letter of subsequent words capitalized, e.g.,
compareToOther or getChangesGreaterThan. This point is not applied very
consistently in Buildbot, but let's try to be consistent in new code.

If you're writing a method that doesn't currently block, but could conceivably
block sometime in the future, return a Deferred and document that it does so.
Just about anything might block - even getters and setters!

Invariably, this extra method gets separated from its parent as the code
evolves, and the result is completely unreadable. Instead, include all of the
code for a particular function or method within the same indented block, using
nested functions:

defgetRevInfo(revname):results={}d=defer.succeed(None)defrev_parse(_):# note use of '_' to quietly indicate an ignored parameterreturnutils.getProcessOutput(git,['rev-parse',revname])d.addCallback(rev_parse)defparse_rev_parse(res):results['rev']=res.strip()returnutils.getProcessOutput(git,['log','-1','--format=%s%n%b',results['rev']])d.addCallback(parse_rev_parse)defparse_log(res):results['comments']=res.strip()d.addCallback(parse_log)defset_results(_):returnresultsd.addCallback(set_results)returnd

it is usually best to make the first operation occur within a callback, as the
deferred machinery will then handle any exceptions as a failure in the outer
Deferred. As a shortcut, d.addCallback works as a decorator:

d=defer.succeed(None)@d.addCallbackdefrev_parse(_):# note use of '_' to quietly indicate an ignored parameterreturnutils.getProcessOutput(git,['rev-parse',revname])

Be careful with local variables. For example, if parse_rev_parse, above,
merely assigned rev=res.strip(), then that variable would be local to
parse_rev_parse and not available in set_results. Mutable variables
(dicts and lists) at the outer function level are appropriate for this purpose.

Note

do not try to build a loop in this style by chaining multiple
Deferreds! Unbounded chaining can result in stack overflows, at least on older
versions of Twisted. Use deferredGenerator instead.

twisted.internet.defer.inlineCallbacks is a great help to writing code
that makes a lot of asynchronous calls, particularly if those calls are made in
loop or conditionals. Refer to the Twisted documentation for the details, but
the style within Buildbot is as follows:

In most cases, the result of a yield expression should be assigned to a
variable. It can be used in a larger expression, but remember that Python
requires that you enclose the expression in its own set of parentheses.

Python does not permit returning a value from a generator, so statements like
returnxval+y are invalid. Instead, yield the result of
defer.returnValue. Although this function does cause an immediate
function exit, for clarity follow it with a bare return, as in
the example, unless it is the last statement in a function.

The great advantage of inlineCallbacks is that it allows you to use all
of the usual Pythonic control structures in their natural form. In particular,
it is easy to represent a loop, or even nested loops, in this style without
losing any readability.

Note that code using deferredGenerator is no longer acceptable in Buildbot.

Remember that asynchronous programming does not free you from the need to worry
about concurrency issues. Particularly if you are executing a sequence of
operations, each time you wait for a Deferred, arbitrary other actions can take
place.

In general, you should try to perform actions atomically, but for the rare
situations that require synchronization, the following might be useful: