We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Move to a ChangeHook in hgbuildbot.py (closes #2227)
Starting a Twisted reactor within a Mercurial hook is problematic
because a hook can get called multiple times (from the same Mercurial
process) which conflicts with the one-time only nature of the Twisted
reactor start/stop calls.

By moving to HTTP requests the hook is now re-entrant which fixes
ReactorNotRestartable exceptions being raised all the time.

The requests library is used to make the calls, since it has a
functional TLS implementation (unlike urllib/httplib in Python 2 until
recently).

Additionally, the codebase argument is added to the base ChangeHook, so
it can properly be used with a codebaseGenerator.

Finally, if a venv argument is used, it is now properly used to activate
the environment [1].

Possible improvements to this changeset are:
- send requests in parallel;
- switch back to urllib2 so the hook doesn't depend on anything;
- a few more cleanups.

[1] https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate_this.py


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `master/buildbot/steps/source/cvs.py`
Content:
```
1 # This file is part of Buildbot.  Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Copyright Buildbot Team Members
15 
16 from email.Utils import formatdate
17 import time
18 
19 from twisted.python import log
20 from twisted.internet import defer
21 
22 from buildbot.process import buildstep
23 from buildbot.steps.shell import StringFileWriter
24 from buildbot.steps.source.base import Source
25 from buildbot.interfaces import BuildSlaveTooOldError
26 
27 class CVS(Source):
28 
29     name = "cvs"
30 
31     renderables = [ "cvsroot" ]
32 
33     def __init__(self, cvsroot=None, cvsmodule='', mode='incremental',
34                  method=None, branch=None, global_options=[], extra_options=[],
35                  login=None, **kwargs):
36 
37         self.cvsroot = cvsroot
38         self.cvsmodule = cvsmodule
39         self.branch = branch
40         self.global_options = global_options
41         self.extra_options = extra_options
42         self.login = login
43         self.mode = mode
44         self.method = method
45         self.srcdir = 'source'
46         Source.__init__(self, **kwargs)
47 
48     def startVC(self, branch, revision, patch):
49         self.revision = revision
50         self.stdio_log = self.addLog("stdio")
51         self.method = self._getMethod()
52         d = self.checkCvs()
53         def checkInstall(cvsInstalled):
54             if not cvsInstalled:
55                 raise BuildSlaveTooOldError("CVS is not installed on slave")
56             return 0
57         d.addCallback(checkInstall)
58         d.addCallback(self.checkLogin)
59 
60         if self.mode == 'incremental':
61             d.addCallback(lambda _: self.incremental())
62         elif self.mode == 'full':
63             d.addCallback(lambda _: self.full())
64 
65         d.addCallback(self.parseGotRevision)
66         d.addCallback(self.finish)
67         d.addErrback(self.failed)
68         return d
69 
70     @defer.inlineCallbacks
71     def incremental(self):
72         updatable = yield self._sourcedirIsUpdatable()
73         if updatable:
74             rv = yield self.doUpdate()
75         else:
76             rv = yield self.clobber()
77         defer.returnValue(rv)
78 
79     @defer.inlineCallbacks
80     def full(self):
81         if self.method == 'clobber':
82             rv = yield self.clobber()
83             defer.returnValue(rv)
84             return
85 
86         elif self.method == 'copy':
87             rv = yield self.copy()
88             defer.returnValue(rv)
89             return
90 
91         updatable = yield self._sourcedirIsUpdatable()
92         if not updatable:
93             log.msg("CVS repo not present, making full checkout")
94             rv = yield self.doCheckout(self.workdir)
95         elif self.method == 'clean':
96             rv = yield self.clean()
97         elif self.method == 'fresh':
98             rv = yield self.fresh()
99         else:
100             raise ValueError("Unknown method, check your configuration")
101         defer.returnValue(rv)
102 
103     def clobber(self):
104         cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,
105                                                 'logEnviron': self.logEnviron})
106         cmd.useLog(self.stdio_log, False)
107         d = self.runCommand(cmd)
108         def checkRemoval(res):
109             if res != 0:
110                 raise RuntimeError("Failed to delete directory")
111             return res
112         d.addCallback(lambda _: checkRemoval(cmd.rc))
113         d.addCallback(lambda _: self.doCheckout(self.workdir))
114         return d
115 
116     def fresh(self, ):
117         d = self.purge(True)
118         d.addCallback(lambda _: self.doUpdate())
119         return d
120 
121     def clean(self, ):
122         d = self.purge(False)
123         d.addCallback(lambda _: self.doUpdate())
124         return d
125 
126     def copy(self):
127         cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,
128                                                 'logEnviron': self.logEnviron})
129         cmd.useLog(self.stdio_log, False)
130         d = self.runCommand(cmd)        
131         self.workdir = 'source'
132         d.addCallback(lambda _: self.incremental())
133         def copy(_):
134             cmd = buildstep.RemoteCommand('cpdir',
135                                           {'fromdir': 'source',
136                                            'todir':'build',
137                                            'logEnviron': self.logEnviron,})
138             cmd.useLog(self.stdio_log, False)
139             d = self.runCommand(cmd)
140             return d
141         d.addCallback(copy)
142         def resetWorkdir(_):
143             self.workdir = 'build'
144             return 0
145         d.addCallback(resetWorkdir)
146         return d
147         
148     def purge(self, ignore_ignores):
149         command = ['cvsdiscard']
150         if ignore_ignores:
151             command += ['--ignore']
152         cmd = buildstep.RemoteShellCommand(self.workdir, command,
153                                            env=self.env,
154                                            logEnviron=self.logEnviron)
155         cmd.useLog(self.stdio_log, False)
156         d = self.runCommand(cmd)
157         def evaluate(rc):
158             if rc != 0:
159                 raise buildstep.BuildStepFailed()
160             return rc
161         d.addCallback(lambda _: evaluate(cmd.rc))
162         return d
163         
164     def doCheckout(self, dir):
165         command = ['-d', self.cvsroot, '-z3', 'checkout', '-d', dir ]
166         command = self.global_options + command + self.extra_options
167         if self.branch:
168             command += ['-r', self.branch]
169         if self.revision:
170             command += ['-D', self.revision]
171         command += [ self.cvsmodule ]
172         d = self._dovccmd(command, '')
173         return d
174 
175     def doUpdate(self):
176         command = ['-z3', 'update', '-dP']
177         if self.branch:
178             command += ['-r', self.branch]
179         if self.revision:
180             command += ['-D', self.revision]
181         d = self._dovccmd(command)
182         return d
183 
184     def finish(self, res):
185         d = defer.succeed(res)
186         def _gotResults(results):
187             self.setStatus(self.cmd, results)
188             return results
189         d.addCallback(_gotResults)
190         d.addCallbacks(self.finished, self.checkDisconnect)
191         return d
192 
193     def checkLogin(self, _):
194         if self.login:
195             d = defer.succeed(0)
196         else:
197             d = self._dovccmd(['-d', self.cvsroot, 'login'])
198             def setLogin(res):
199                 # this happens only if the login command succeeds.
200                 self.login = True
201                 return res
202             d.addCallback(setLogin)
203 
204         return d
205 
206     def _dovccmd(self, command, workdir=None):
207         if workdir is None:
208             workdir = self.workdir
209         if not command:
210             raise ValueError("No command specified")
211         cmd = buildstep.RemoteShellCommand(workdir, ['cvs'] +
212                                            command,
213                                            env=self.env,
214                                            logEnviron=self.logEnviron)
215         cmd.useLog(self.stdio_log, False)
216         d = self.runCommand(cmd)
217         def evaluateCommand(cmd):
218             if cmd.rc != 0:
219                 log.msg("Source step failed while running command %s" % cmd)
220                 raise buildstep.BuildStepFailed()
221             return cmd.rc
222         d.addCallback(lambda _: evaluateCommand(cmd))
223         return d
224 
225     @defer.inlineCallbacks
226     def _sourcedirIsUpdatable(self):
227         myFileWriter = StringFileWriter()
228         args = {
229                 'workdir': self.build.path_module.join(self.workdir, 'CVS'),
230                 'writer': myFileWriter,
231                 'maxsize': None,
232                 'blocksize': 32*1024,
233                 }
234 
235         cmd = buildstep.RemoteCommand('uploadFile',
236                 dict(slavesrc='Root', **args),
237                 ignore_updates=True)
238         yield self.runCommand(cmd)
239         if cmd.rc is not None and cmd.rc != 0:
240             defer.returnValue(False)
241             return
242         if myFileWriter.buffer.strip() != self.cvsroot:
243             defer.returnValue(False)
244             return
245 
246         myFileWriter.buffer = ""
247         cmd = buildstep.RemoteCommand('uploadFile',
248                 dict(slavesrc='Repository', **args),
249                 ignore_updates=True)
250         yield self.runCommand(cmd)
251         if cmd.rc is not None and cmd.rc != 0:
252             defer.returnValue(False)
253             return
254         if myFileWriter.buffer.strip() != self.cvsmodule:
255             defer.returnValue(False)
256             return
257 
258         defer.returnValue(True)
259 
260     def parseGotRevision(self, res):
261         revision = time.strftime("%Y-%m-%d %H:%M:%S +0000", time.gmtime())
262         self.setProperty('got_revision', revision, 'Source')
263         return res
264 
265     def checkCvs(self):
266         d = self._dovccmd(['--version'])
267         def check(res):
268             if res == 0:
269                 return True
270             return False
271         d.addCallback(check)
272         return d
273 
274     def _getMethod(self):
275         if self.method is not None and self.mode != 'incremental':
276             return self.method
277         elif self.mode == 'incremental':
278             return None
279         elif self.method is None and self.mode == 'full':
280             return 'fresh'
281 
282     def computeSourceRevision(self, changes):
283         if not changes:
284             return None
285         lastChange = max([c.when for c in changes])
286         lastSubmit = max([br.submittedAt for br in self.build.requests])
287         when = (lastChange + lastSubmit) / 2
288         return formatdate(when)
289
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/master/buildbot/steps/source/cvs.py b/master/buildbot/steps/source/cvs.py
--- a/master/buildbot/steps/source/cvs.py
+++ b/master/buildbot/steps/source/cvs.py
@@ -46,6 +46,7 @@
         Source.__init__(self, **kwargs)
 
     def startVC(self, branch, revision, patch):
+        self.branch = branch
         self.revision = revision
         self.stdio_log = self.addLog("stdio")
         self.method = self._getMethod()

{"golden_diff": "diff --git a/master/buildbot/steps/source/cvs.py b/master/buildbot/steps/source/cvs.py\n--- a/master/buildbot/steps/source/cvs.py\n+++ b/master/buildbot/steps/source/cvs.py\n@@ -46,6 +46,7 @@\n         Source.__init__(self, **kwargs)\n \n     def startVC(self, branch, revision, patch):\n+        self.branch = branch\n         self.revision = revision\n         self.stdio_log = self.addLog(\"stdio\")\n         self.method = self._getMethod()\n", "issue": "Move to a ChangeHook in hgbuildbot.py (closes #2227)\nStarting a Twisted reactor within a Mercurial hook is problematic\nbecause a hook can get called multiple times (from the same Mercurial\nprocess) which conflicts with the one-time only nature of the Twisted\nreactor start/stop calls.\n\nBy moving to HTTP requests the hook is now re-entrant which fixes\nReactorNotRestartable exceptions being raised all the time.\n\nThe requests library is used to make the calls, since it has a\nfunctional TLS implementation (unlike urllib/httplib in Python 2 until\nrecently).\n\nAdditionally, the codebase argument is added to the base ChangeHook, so\nit can properly be used with a codebaseGenerator.\n\nFinally, if a venv argument is used, it is now properly used to activate\nthe environment [1].\n\nPossible improvements to this changeset are:\n- send requests in parallel;\n- switch back to urllib2 so the hook doesn't depend on anything;\n- a few more cleanups.\n\n[1] https://github.com/pypa/virtualenv/blob/master/virtualenv_embedded/activate_this.py\n\n", "before_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom email.Utils import formatdate\nimport time\n\nfrom twisted.python import log\nfrom twisted.internet import defer\n\nfrom buildbot.process import buildstep\nfrom buildbot.steps.shell import StringFileWriter\nfrom buildbot.steps.source.base import Source\nfrom buildbot.interfaces import BuildSlaveTooOldError\n\nclass CVS(Source):\n\n    name = \"cvs\"\n\n    renderables = [ \"cvsroot\" ]\n\n    def __init__(self, cvsroot=None, cvsmodule='', mode='incremental',\n                 method=None, branch=None, global_options=[], extra_options=[],\n                 login=None, **kwargs):\n\n        self.cvsroot = cvsroot\n        self.cvsmodule = cvsmodule\n        self.branch = branch\n        self.global_options = global_options\n        self.extra_options = extra_options\n        self.login = login\n        self.mode = mode\n        self.method = method\n        self.srcdir = 'source'\n        Source.__init__(self, **kwargs)\n\n    def startVC(self, branch, revision, patch):\n        self.revision = revision\n        self.stdio_log = self.addLog(\"stdio\")\n        self.method = self._getMethod()\n        d = self.checkCvs()\n        def checkInstall(cvsInstalled):\n            if not cvsInstalled:\n                raise BuildSlaveTooOldError(\"CVS is not installed on slave\")\n            return 0\n        d.addCallback(checkInstall)\n        d.addCallback(self.checkLogin)\n\n        if self.mode == 'incremental':\n            d.addCallback(lambda _: self.incremental())\n        elif self.mode == 'full':\n            d.addCallback(lambda _: self.full())\n\n        d.addCallback(self.parseGotRevision)\n        d.addCallback(self.finish)\n        d.addErrback(self.failed)\n        return d\n\n    @defer.inlineCallbacks\n    def incremental(self):\n        updatable = yield self._sourcedirIsUpdatable()\n        if updatable:\n            rv = yield self.doUpdate()\n        else:\n            rv = yield self.clobber()\n        defer.returnValue(rv)\n\n    @defer.inlineCallbacks\n    def full(self):\n        if self.method == 'clobber':\n            rv = yield self.clobber()\n            defer.returnValue(rv)\n            return\n\n        elif self.method == 'copy':\n            rv = yield self.copy()\n            defer.returnValue(rv)\n            return\n\n        updatable = yield self._sourcedirIsUpdatable()\n        if not updatable:\n            log.msg(\"CVS repo not present, making full checkout\")\n            rv = yield self.doCheckout(self.workdir)\n        elif self.method == 'clean':\n            rv = yield self.clean()\n        elif self.method == 'fresh':\n            rv = yield self.fresh()\n        else:\n            raise ValueError(\"Unknown method, check your configuration\")\n        defer.returnValue(rv)\n\n    def clobber(self):\n        cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,\n                                                'logEnviron': self.logEnviron})\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def checkRemoval(res):\n            if res != 0:\n                raise RuntimeError(\"Failed to delete directory\")\n            return res\n        d.addCallback(lambda _: checkRemoval(cmd.rc))\n        d.addCallback(lambda _: self.doCheckout(self.workdir))\n        return d\n\n    def fresh(self, ):\n        d = self.purge(True)\n        d.addCallback(lambda _: self.doUpdate())\n        return d\n\n    def clean(self, ):\n        d = self.purge(False)\n        d.addCallback(lambda _: self.doUpdate())\n        return d\n\n    def copy(self):\n        cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,\n                                                'logEnviron': self.logEnviron})\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)        \n        self.workdir = 'source'\n        d.addCallback(lambda _: self.incremental())\n        def copy(_):\n            cmd = buildstep.RemoteCommand('cpdir',\n                                          {'fromdir': 'source',\n                                           'todir':'build',\n                                           'logEnviron': self.logEnviron,})\n            cmd.useLog(self.stdio_log, False)\n            d = self.runCommand(cmd)\n            return d\n        d.addCallback(copy)\n        def resetWorkdir(_):\n            self.workdir = 'build'\n            return 0\n        d.addCallback(resetWorkdir)\n        return d\n        \n    def purge(self, ignore_ignores):\n        command = ['cvsdiscard']\n        if ignore_ignores:\n            command += ['--ignore']\n        cmd = buildstep.RemoteShellCommand(self.workdir, command,\n                                           env=self.env,\n                                           logEnviron=self.logEnviron)\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def evaluate(rc):\n            if rc != 0:\n                raise buildstep.BuildStepFailed()\n            return rc\n        d.addCallback(lambda _: evaluate(cmd.rc))\n        return d\n        \n    def doCheckout(self, dir):\n        command = ['-d', self.cvsroot, '-z3', 'checkout', '-d', dir ]\n        command = self.global_options + command + self.extra_options\n        if self.branch:\n            command += ['-r', self.branch]\n        if self.revision:\n            command += ['-D', self.revision]\n        command += [ self.cvsmodule ]\n        d = self._dovccmd(command, '')\n        return d\n\n    def doUpdate(self):\n        command = ['-z3', 'update', '-dP']\n        if self.branch:\n            command += ['-r', self.branch]\n        if self.revision:\n            command += ['-D', self.revision]\n        d = self._dovccmd(command)\n        return d\n\n    def finish(self, res):\n        d = defer.succeed(res)\n        def _gotResults(results):\n            self.setStatus(self.cmd, results)\n            return results\n        d.addCallback(_gotResults)\n        d.addCallbacks(self.finished, self.checkDisconnect)\n        return d\n\n    def checkLogin(self, _):\n        if self.login:\n            d = defer.succeed(0)\n        else:\n            d = self._dovccmd(['-d', self.cvsroot, 'login'])\n            def setLogin(res):\n                # this happens only if the login command succeeds.\n                self.login = True\n                return res\n            d.addCallback(setLogin)\n\n        return d\n\n    def _dovccmd(self, command, workdir=None):\n        if workdir is None:\n            workdir = self.workdir\n        if not command:\n            raise ValueError(\"No command specified\")\n        cmd = buildstep.RemoteShellCommand(workdir, ['cvs'] +\n                                           command,\n                                           env=self.env,\n                                           logEnviron=self.logEnviron)\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def evaluateCommand(cmd):\n            if cmd.rc != 0:\n                log.msg(\"Source step failed while running command %s\" % cmd)\n                raise buildstep.BuildStepFailed()\n            return cmd.rc\n        d.addCallback(lambda _: evaluateCommand(cmd))\n        return d\n\n    @defer.inlineCallbacks\n    def _sourcedirIsUpdatable(self):\n        myFileWriter = StringFileWriter()\n        args = {\n                'workdir': self.build.path_module.join(self.workdir, 'CVS'),\n                'writer': myFileWriter,\n                'maxsize': None,\n                'blocksize': 32*1024,\n                }\n\n        cmd = buildstep.RemoteCommand('uploadFile',\n                dict(slavesrc='Root', **args),\n                ignore_updates=True)\n        yield self.runCommand(cmd)\n        if cmd.rc is not None and cmd.rc != 0:\n            defer.returnValue(False)\n            return\n        if myFileWriter.buffer.strip() != self.cvsroot:\n            defer.returnValue(False)\n            return\n\n        myFileWriter.buffer = \"\"\n        cmd = buildstep.RemoteCommand('uploadFile',\n                dict(slavesrc='Repository', **args),\n                ignore_updates=True)\n        yield self.runCommand(cmd)\n        if cmd.rc is not None and cmd.rc != 0:\n            defer.returnValue(False)\n            return\n        if myFileWriter.buffer.strip() != self.cvsmodule:\n            defer.returnValue(False)\n            return\n\n        defer.returnValue(True)\n\n    def parseGotRevision(self, res):\n        revision = time.strftime(\"%Y-%m-%d %H:%M:%S +0000\", time.gmtime())\n        self.setProperty('got_revision', revision, 'Source')\n        return res\n\n    def checkCvs(self):\n        d = self._dovccmd(['--version'])\n        def check(res):\n            if res == 0:\n                return True\n            return False\n        d.addCallback(check)\n        return d\n\n    def _getMethod(self):\n        if self.method is not None and self.mode != 'incremental':\n            return self.method\n        elif self.mode == 'incremental':\n            return None\n        elif self.method is None and self.mode == 'full':\n            return 'fresh'\n\n    def computeSourceRevision(self, changes):\n        if not changes:\n            return None\n        lastChange = max([c.when for c in changes])\n        lastSubmit = max([br.submittedAt for br in self.build.requests])\n        when = (lastChange + lastSubmit) / 2\n        return formatdate(when)\n", "path": "master/buildbot/steps/source/cvs.py"}], "after_files": [{"content": "# This file is part of Buildbot.  Buildbot is free software: you can\n# redistribute it and/or modify it under the terms of the GNU General Public\n# License as published by the Free Software Foundation, version 2.\n#\n# This program is distributed in the hope that it will be useful, but WITHOUT\n# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n# details.\n#\n# You should have received a copy of the GNU General Public License along with\n# this program; if not, write to the Free Software Foundation, Inc., 51\n# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n#\n# Copyright Buildbot Team Members\n\nfrom email.Utils import formatdate\nimport time\n\nfrom twisted.python import log\nfrom twisted.internet import defer\n\nfrom buildbot.process import buildstep\nfrom buildbot.steps.shell import StringFileWriter\nfrom buildbot.steps.source.base import Source\nfrom buildbot.interfaces import BuildSlaveTooOldError\n\nclass CVS(Source):\n\n    name = \"cvs\"\n\n    renderables = [ \"cvsroot\" ]\n\n    def __init__(self, cvsroot=None, cvsmodule='', mode='incremental',\n                 method=None, branch=None, global_options=[], extra_options=[],\n                 login=None, **kwargs):\n\n        self.cvsroot = cvsroot\n        self.cvsmodule = cvsmodule\n        self.branch = branch\n        self.global_options = global_options\n        self.extra_options = extra_options\n        self.login = login\n        self.mode = mode\n        self.method = method\n        self.srcdir = 'source'\n        Source.__init__(self, **kwargs)\n\n    def startVC(self, branch, revision, patch):\n        self.branch = branch\n        self.revision = revision\n        self.stdio_log = self.addLog(\"stdio\")\n        self.method = self._getMethod()\n        d = self.checkCvs()\n        def checkInstall(cvsInstalled):\n            if not cvsInstalled:\n                raise BuildSlaveTooOldError(\"CVS is not installed on slave\")\n            return 0\n        d.addCallback(checkInstall)\n        d.addCallback(self.checkLogin)\n\n        if self.mode == 'incremental':\n            d.addCallback(lambda _: self.incremental())\n        elif self.mode == 'full':\n            d.addCallback(lambda _: self.full())\n\n        d.addCallback(self.parseGotRevision)\n        d.addCallback(self.finish)\n        d.addErrback(self.failed)\n        return d\n\n    @defer.inlineCallbacks\n    def incremental(self):\n        updatable = yield self._sourcedirIsUpdatable()\n        if updatable:\n            rv = yield self.doUpdate()\n        else:\n            rv = yield self.clobber()\n        defer.returnValue(rv)\n\n    @defer.inlineCallbacks\n    def full(self):\n        if self.method == 'clobber':\n            rv = yield self.clobber()\n            defer.returnValue(rv)\n            return\n\n        elif self.method == 'copy':\n            rv = yield self.copy()\n            defer.returnValue(rv)\n            return\n\n        updatable = yield self._sourcedirIsUpdatable()\n        if not updatable:\n            log.msg(\"CVS repo not present, making full checkout\")\n            rv = yield self.doCheckout(self.workdir)\n        elif self.method == 'clean':\n            rv = yield self.clean()\n        elif self.method == 'fresh':\n            rv = yield self.fresh()\n        else:\n            raise ValueError(\"Unknown method, check your configuration\")\n        defer.returnValue(rv)\n\n    def clobber(self):\n        cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,\n                                                'logEnviron': self.logEnviron})\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def checkRemoval(res):\n            if res != 0:\n                raise RuntimeError(\"Failed to delete directory\")\n            return res\n        d.addCallback(lambda _: checkRemoval(cmd.rc))\n        d.addCallback(lambda _: self.doCheckout(self.workdir))\n        return d\n\n    def fresh(self, ):\n        d = self.purge(True)\n        d.addCallback(lambda _: self.doUpdate())\n        return d\n\n    def clean(self, ):\n        d = self.purge(False)\n        d.addCallback(lambda _: self.doUpdate())\n        return d\n\n    def copy(self):\n        cmd = buildstep.RemoteCommand('rmdir', {'dir': self.workdir,\n                                                'logEnviron': self.logEnviron})\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)        \n        self.workdir = 'source'\n        d.addCallback(lambda _: self.incremental())\n        def copy(_):\n            cmd = buildstep.RemoteCommand('cpdir',\n                                          {'fromdir': 'source',\n                                           'todir':'build',\n                                           'logEnviron': self.logEnviron,})\n            cmd.useLog(self.stdio_log, False)\n            d = self.runCommand(cmd)\n            return d\n        d.addCallback(copy)\n        def resetWorkdir(_):\n            self.workdir = 'build'\n            return 0\n        d.addCallback(resetWorkdir)\n        return d\n        \n    def purge(self, ignore_ignores):\n        command = ['cvsdiscard']\n        if ignore_ignores:\n            command += ['--ignore']\n        cmd = buildstep.RemoteShellCommand(self.workdir, command,\n                                           env=self.env,\n                                           logEnviron=self.logEnviron)\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def evaluate(rc):\n            if rc != 0:\n                raise buildstep.BuildStepFailed()\n            return rc\n        d.addCallback(lambda _: evaluate(cmd.rc))\n        return d\n        \n    def doCheckout(self, dir):\n        command = ['-d', self.cvsroot, '-z3', 'checkout', '-d', dir ]\n        command = self.global_options + command + self.extra_options\n        if self.branch:\n            command += ['-r', self.branch]\n        if self.revision:\n            command += ['-D', self.revision]\n        command += [ self.cvsmodule ]\n        d = self._dovccmd(command, '')\n        return d\n\n    def doUpdate(self):\n        command = ['-z3', 'update', '-dP']\n        if self.branch:\n            command += ['-r', self.branch]\n        if self.revision:\n            command += ['-D', self.revision]\n        d = self._dovccmd(command)\n        return d\n\n    def finish(self, res):\n        d = defer.succeed(res)\n        def _gotResults(results):\n            self.setStatus(self.cmd, results)\n            return results\n        d.addCallback(_gotResults)\n        d.addCallbacks(self.finished, self.checkDisconnect)\n        return d\n\n    def checkLogin(self, _):\n        if self.login:\n            d = defer.succeed(0)\n        else:\n            d = self._dovccmd(['-d', self.cvsroot, 'login'])\n            def setLogin(res):\n                # this happens only if the login command succeeds.\n                self.login = True\n                return res\n            d.addCallback(setLogin)\n\n        return d\n\n    def _dovccmd(self, command, workdir=None):\n        if workdir is None:\n            workdir = self.workdir\n        if not command:\n            raise ValueError(\"No command specified\")\n        cmd = buildstep.RemoteShellCommand(workdir, ['cvs'] +\n                                           command,\n                                           env=self.env,\n                                           logEnviron=self.logEnviron)\n        cmd.useLog(self.stdio_log, False)\n        d = self.runCommand(cmd)\n        def evaluateCommand(cmd):\n            if cmd.rc != 0:\n                log.msg(\"Source step failed while running command %s\" % cmd)\n                raise buildstep.BuildStepFailed()\n            return cmd.rc\n        d.addCallback(lambda _: evaluateCommand(cmd))\n        return d\n\n    @defer.inlineCallbacks\n    def _sourcedirIsUpdatable(self):\n        myFileWriter = StringFileWriter()\n        args = {\n                'workdir': self.build.path_module.join(self.workdir, 'CVS'),\n                'writer': myFileWriter,\n                'maxsize': None,\n                'blocksize': 32*1024,\n                }\n\n        cmd = buildstep.RemoteCommand('uploadFile',\n                dict(slavesrc='Root', **args),\n                ignore_updates=True)\n        yield self.runCommand(cmd)\n        if cmd.rc is not None and cmd.rc != 0:\n            defer.returnValue(False)\n            return\n        if myFileWriter.buffer.strip() != self.cvsroot:\n            defer.returnValue(False)\n            return\n\n        myFileWriter.buffer = \"\"\n        cmd = buildstep.RemoteCommand('uploadFile',\n                dict(slavesrc='Repository', **args),\n                ignore_updates=True)\n        yield self.runCommand(cmd)\n        if cmd.rc is not None and cmd.rc != 0:\n            defer.returnValue(False)\n            return\n        if myFileWriter.buffer.strip() != self.cvsmodule:\n            defer.returnValue(False)\n            return\n\n        defer.returnValue(True)\n\n    def parseGotRevision(self, res):\n        revision = time.strftime(\"%Y-%m-%d %H:%M:%S +0000\", time.gmtime())\n        self.setProperty('got_revision', revision, 'Source')\n        return res\n\n    def checkCvs(self):\n        d = self._dovccmd(['--version'])\n        def check(res):\n            if res == 0:\n                return True\n            return False\n        d.addCallback(check)\n        return d\n\n    def _getMethod(self):\n        if self.method is not None and self.mode != 'incremental':\n            return self.method\n        elif self.mode == 'incremental':\n            return None\n        elif self.method is None and self.mode == 'full':\n            return 'fresh'\n\n    def computeSourceRevision(self, changes):\n        if not changes:\n            return None\n        lastChange = max([c.when for c in changes])\n        lastSubmit = max([br.submittedAt for br in self.build.requests])\n        when = (lastChange + lastSubmit) / 2\n        return formatdate(when)\n", "path": "master/buildbot/steps/source/cvs.py"}]}