We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Expose builder.tags to web templates.
This tiny PR exposes the builders' "tags" attribute to the templates, allowing me to customize the templates using that feature.


--- 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/master.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 import os, types, re
17 from twisted.python import runtime
18 from twisted.internet import reactor
19 from buildbot.process.buildstep import BuildStep
20 from buildbot.process.buildstep import SUCCESS, FAILURE
21 from twisted.internet.protocol import ProcessProtocol
22 
23 class MasterShellCommand(BuildStep):
24     """
25     Run a shell command locally - on the buildmaster.  The shell command
26     COMMAND is specified just as for a RemoteShellCommand.  Note that extra
27     logfiles are not supported.
28     """
29     name='MasterShellCommand'
30     description='Running'
31     descriptionDone='Ran'
32     descriptionSuffix = None
33     renderables = [ 'command', 'env', 'description', 'descriptionDone', 'descriptionSuffix' ]
34     haltOnFailure = True
35     flunkOnFailure = True
36 
37     def __init__(self, command,
38                  description=None, descriptionDone=None, descriptionSuffix=None,
39                  env=None, path=None, usePTY=0,
40                  **kwargs):
41         BuildStep.__init__(self, **kwargs)
42 
43         self.command=command
44         if description:
45             self.description = description
46         if isinstance(self.description, str):
47             self.description = [self.description]
48         if descriptionDone:
49             self.descriptionDone = descriptionDone
50         if isinstance(self.descriptionDone, str):
51             self.descriptionDone = [self.descriptionDone]
52         if descriptionSuffix:
53             self.descriptionSuffix = descriptionSuffix
54         if isinstance(self.descriptionSuffix, str):
55             self.descriptionSuffix = [self.descriptionSuffix]
56         self.env=env
57         self.path=path
58         self.usePTY=usePTY
59 
60     class LocalPP(ProcessProtocol):
61         def __init__(self, step):
62             self.step = step
63 
64         def outReceived(self, data):
65             self.step.stdio_log.addStdout(data)
66 
67         def errReceived(self, data):
68             self.step.stdio_log.addStderr(data)
69 
70         def processEnded(self, status_object):
71             self.step.stdio_log.addHeader("exit status %d\n" % status_object.value.exitCode)
72             self.step.processEnded(status_object)
73 
74     def start(self):
75         # render properties
76         command = self.command
77         # set up argv
78         if type(command) in types.StringTypes:
79             if runtime.platformType  == 'win32':
80                 argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
81                 if '/c' not in argv: argv += ['/c']
82                 argv += [command]
83             else:
84                 # for posix, use /bin/sh. for other non-posix, well, doesn't
85                 # hurt to try
86                 argv = ['/bin/sh', '-c', command]
87         else:
88             if runtime.platformType  == 'win32':
89                 argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args
90                 if '/c' not in argv: argv += ['/c']
91                 argv += list(command)
92             else:
93                 argv = command
94 
95         self.stdio_log = stdio_log = self.addLog("stdio")
96 
97         if type(command) in types.StringTypes:
98             stdio_log.addHeader(command.strip() + "\n\n")
99         else:
100             stdio_log.addHeader(" ".join(command) + "\n\n")
101         stdio_log.addHeader("** RUNNING ON BUILDMASTER **\n")
102         stdio_log.addHeader(" in dir %s\n" % os.getcwd())
103         stdio_log.addHeader(" argv: %s\n" % (argv,))
104         self.step_status.setText(self.describe())
105 
106         if self.env is None:
107             env = os.environ
108         else:
109             assert isinstance(self.env, dict)
110             env = self.env
111 
112             # do substitution on variable values matching pattern: ${name}
113             p = re.compile('\${([0-9a-zA-Z_]*)}')
114             def subst(match):
115                 return os.environ.get(match.group(1), "")
116             newenv = {}
117             for key in env.keys():
118                 if env[key] is not None:
119                     newenv[key] = p.sub(subst, env[key])
120             env = newenv
121         stdio_log.addHeader(" env: %r\n" % (env,))
122 
123         # TODO add a timeout?
124         reactor.spawnProcess(self.LocalPP(self), argv[0], argv,
125                 path=self.path, usePTY=self.usePTY, env=env )
126         # (the LocalPP object will call processEnded for us)
127 
128     def processEnded(self, status_object):
129         if status_object.value.exitCode != 0:
130             self.descriptionDone = ["failed (%d)" % status_object.value.exitCode]
131             self.step_status.setText(self.describe(done=True))
132             self.finished(FAILURE)
133         else:
134             self.step_status.setText(self.describe(done=True))
135             self.finished(SUCCESS)
136 
137     def describe(self, done=False):
138         desc = self.descriptionDone if done else self.description
139         if self.descriptionSuffix:
140             desc = desc[:]
141             desc.extend(self.descriptionSuffix)
142         return desc
143
```
--- 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/master.py b/master/buildbot/steps/master.py
--- a/master/buildbot/steps/master.py
+++ b/master/buildbot/steps/master.py
@@ -18,6 +18,7 @@
 from twisted.internet import reactor
 from buildbot.process.buildstep import BuildStep
 from buildbot.process.buildstep import SUCCESS, FAILURE
+from twisted.internet import error
 from twisted.internet.protocol import ProcessProtocol
 
 class MasterShellCommand(BuildStep):
@@ -36,7 +37,7 @@
 
     def __init__(self, command,
                  description=None, descriptionDone=None, descriptionSuffix=None,
-                 env=None, path=None, usePTY=0,
+                 env=None, path=None, usePTY=0, interruptSignal="KILL",
                  **kwargs):
         BuildStep.__init__(self, **kwargs)
 
@@ -56,6 +57,7 @@
         self.env=env
         self.path=path
         self.usePTY=usePTY
+        self.interruptSignal = interruptSignal
 
     class LocalPP(ProcessProtocol):
         def __init__(self, step):
@@ -68,7 +70,10 @@
             self.step.stdio_log.addStderr(data)
 
         def processEnded(self, status_object):
-            self.step.stdio_log.addHeader("exit status %d\n" % status_object.value.exitCode)
+            if status_object.value.exitCode is not None:
+                self.step.stdio_log.addHeader("exit status %d\n" % status_object.value.exitCode)
+            if status_object.value.signal is not None:
+                self.step.stdio_log.addHeader("signal %s\n" % status_object.value.signal)
             self.step.processEnded(status_object)
 
     def start(self):
@@ -121,12 +126,16 @@
         stdio_log.addHeader(" env: %r\n" % (env,))
 
         # TODO add a timeout?
-        reactor.spawnProcess(self.LocalPP(self), argv[0], argv,
+        self.process = reactor.spawnProcess(self.LocalPP(self), argv[0], argv,
                 path=self.path, usePTY=self.usePTY, env=env )
         # (the LocalPP object will call processEnded for us)
 
     def processEnded(self, status_object):
-        if status_object.value.exitCode != 0:
+        if status_object.value.signal is not None:
+            self.descriptionDone = ["killed (%s)" % status_object.value.signal]
+            self.step_status.setText(self.describe(done=True))
+            self.finished(FAILURE)
+        elif status_object.value.exitCode != 0:
             self.descriptionDone = ["failed (%d)" % status_object.value.exitCode]
             self.step_status.setText(self.describe(done=True))
             self.finished(FAILURE)
@@ -140,3 +149,12 @@
             desc = desc[:]
             desc.extend(self.descriptionSuffix)
         return desc
+
+    def interrupt(self, reason):
+        try:
+            self.process.signalProcess(self.interruptSignal)
+        except KeyError: # Process not started yet
+            pass
+        except error.ProcessExitedAlready:
+            pass
+        BuildStep.interrupt(self, reason)

{"golden_diff": "diff --git a/master/buildbot/steps/master.py b/master/buildbot/steps/master.py\n--- a/master/buildbot/steps/master.py\n+++ b/master/buildbot/steps/master.py\n@@ -18,6 +18,7 @@\n from twisted.internet import reactor\n from buildbot.process.buildstep import BuildStep\n from buildbot.process.buildstep import SUCCESS, FAILURE\n+from twisted.internet import error\n from twisted.internet.protocol import ProcessProtocol\n \n class MasterShellCommand(BuildStep):\n@@ -36,7 +37,7 @@\n \n     def __init__(self, command,\n                  description=None, descriptionDone=None, descriptionSuffix=None,\n-                 env=None, path=None, usePTY=0,\n+                 env=None, path=None, usePTY=0, interruptSignal=\"KILL\",\n                  **kwargs):\n         BuildStep.__init__(self, **kwargs)\n \n@@ -56,6 +57,7 @@\n         self.env=env\n         self.path=path\n         self.usePTY=usePTY\n+        self.interruptSignal = interruptSignal\n \n     class LocalPP(ProcessProtocol):\n         def __init__(self, step):\n@@ -68,7 +70,10 @@\n             self.step.stdio_log.addStderr(data)\n \n         def processEnded(self, status_object):\n-            self.step.stdio_log.addHeader(\"exit status %d\\n\" % status_object.value.exitCode)\n+            if status_object.value.exitCode is not None:\n+                self.step.stdio_log.addHeader(\"exit status %d\\n\" % status_object.value.exitCode)\n+            if status_object.value.signal is not None:\n+                self.step.stdio_log.addHeader(\"signal %s\\n\" % status_object.value.signal)\n             self.step.processEnded(status_object)\n \n     def start(self):\n@@ -121,12 +126,16 @@\n         stdio_log.addHeader(\" env: %r\\n\" % (env,))\n \n         # TODO add a timeout?\n-        reactor.spawnProcess(self.LocalPP(self), argv[0], argv,\n+        self.process = reactor.spawnProcess(self.LocalPP(self), argv[0], argv,\n                 path=self.path, usePTY=self.usePTY, env=env )\n         # (the LocalPP object will call processEnded for us)\n \n     def processEnded(self, status_object):\n-        if status_object.value.exitCode != 0:\n+        if status_object.value.signal is not None:\n+            self.descriptionDone = [\"killed (%s)\" % status_object.value.signal]\n+            self.step_status.setText(self.describe(done=True))\n+            self.finished(FAILURE)\n+        elif status_object.value.exitCode != 0:\n             self.descriptionDone = [\"failed (%d)\" % status_object.value.exitCode]\n             self.step_status.setText(self.describe(done=True))\n             self.finished(FAILURE)\n@@ -140,3 +149,12 @@\n             desc = desc[:]\n             desc.extend(self.descriptionSuffix)\n         return desc\n+\n+    def interrupt(self, reason):\n+        try:\n+            self.process.signalProcess(self.interruptSignal)\n+        except KeyError: # Process not started yet\n+            pass\n+        except error.ProcessExitedAlready:\n+            pass\n+        BuildStep.interrupt(self, reason)\n", "issue": "Expose builder.tags to web templates.\nThis tiny PR exposes the builders' \"tags\" attribute to the templates, allowing me to customize the templates using that feature.\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\nimport os, types, re\nfrom twisted.python import runtime\nfrom twisted.internet import reactor\nfrom buildbot.process.buildstep import BuildStep\nfrom buildbot.process.buildstep import SUCCESS, FAILURE\nfrom twisted.internet.protocol import ProcessProtocol\n\nclass MasterShellCommand(BuildStep):\n    \"\"\"\n    Run a shell command locally - on the buildmaster.  The shell command\n    COMMAND is specified just as for a RemoteShellCommand.  Note that extra\n    logfiles are not supported.\n    \"\"\"\n    name='MasterShellCommand'\n    description='Running'\n    descriptionDone='Ran'\n    descriptionSuffix = None\n    renderables = [ 'command', 'env', 'description', 'descriptionDone', 'descriptionSuffix' ]\n    haltOnFailure = True\n    flunkOnFailure = True\n\n    def __init__(self, command,\n                 description=None, descriptionDone=None, descriptionSuffix=None,\n                 env=None, path=None, usePTY=0,\n                 **kwargs):\n        BuildStep.__init__(self, **kwargs)\n\n        self.command=command\n        if description:\n            self.description = description\n        if isinstance(self.description, str):\n            self.description = [self.description]\n        if descriptionDone:\n            self.descriptionDone = descriptionDone\n        if isinstance(self.descriptionDone, str):\n            self.descriptionDone = [self.descriptionDone]\n        if descriptionSuffix:\n            self.descriptionSuffix = descriptionSuffix\n        if isinstance(self.descriptionSuffix, str):\n            self.descriptionSuffix = [self.descriptionSuffix]\n        self.env=env\n        self.path=path\n        self.usePTY=usePTY\n\n    class LocalPP(ProcessProtocol):\n        def __init__(self, step):\n            self.step = step\n\n        def outReceived(self, data):\n            self.step.stdio_log.addStdout(data)\n\n        def errReceived(self, data):\n            self.step.stdio_log.addStderr(data)\n\n        def processEnded(self, status_object):\n            self.step.stdio_log.addHeader(\"exit status %d\\n\" % status_object.value.exitCode)\n            self.step.processEnded(status_object)\n\n    def start(self):\n        # render properties\n        command = self.command\n        # set up argv\n        if type(command) in types.StringTypes:\n            if runtime.platformType  == 'win32':\n                argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args\n                if '/c' not in argv: argv += ['/c']\n                argv += [command]\n            else:\n                # for posix, use /bin/sh. for other non-posix, well, doesn't\n                # hurt to try\n                argv = ['/bin/sh', '-c', command]\n        else:\n            if runtime.platformType  == 'win32':\n                argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args\n                if '/c' not in argv: argv += ['/c']\n                argv += list(command)\n            else:\n                argv = command\n\n        self.stdio_log = stdio_log = self.addLog(\"stdio\")\n\n        if type(command) in types.StringTypes:\n            stdio_log.addHeader(command.strip() + \"\\n\\n\")\n        else:\n            stdio_log.addHeader(\" \".join(command) + \"\\n\\n\")\n        stdio_log.addHeader(\"** RUNNING ON BUILDMASTER **\\n\")\n        stdio_log.addHeader(\" in dir %s\\n\" % os.getcwd())\n        stdio_log.addHeader(\" argv: %s\\n\" % (argv,))\n        self.step_status.setText(self.describe())\n\n        if self.env is None:\n            env = os.environ\n        else:\n            assert isinstance(self.env, dict)\n            env = self.env\n\n            # do substitution on variable values matching pattern: ${name}\n            p = re.compile('\\${([0-9a-zA-Z_]*)}')\n            def subst(match):\n                return os.environ.get(match.group(1), \"\")\n            newenv = {}\n            for key in env.keys():\n                if env[key] is not None:\n                    newenv[key] = p.sub(subst, env[key])\n            env = newenv\n        stdio_log.addHeader(\" env: %r\\n\" % (env,))\n\n        # TODO add a timeout?\n        reactor.spawnProcess(self.LocalPP(self), argv[0], argv,\n                path=self.path, usePTY=self.usePTY, env=env )\n        # (the LocalPP object will call processEnded for us)\n\n    def processEnded(self, status_object):\n        if status_object.value.exitCode != 0:\n            self.descriptionDone = [\"failed (%d)\" % status_object.value.exitCode]\n            self.step_status.setText(self.describe(done=True))\n            self.finished(FAILURE)\n        else:\n            self.step_status.setText(self.describe(done=True))\n            self.finished(SUCCESS)\n\n    def describe(self, done=False):\n        desc = self.descriptionDone if done else self.description\n        if self.descriptionSuffix:\n            desc = desc[:]\n            desc.extend(self.descriptionSuffix)\n        return desc\n", "path": "master/buildbot/steps/master.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\nimport os, types, re\nfrom twisted.python import runtime\nfrom twisted.internet import reactor\nfrom buildbot.process.buildstep import BuildStep\nfrom buildbot.process.buildstep import SUCCESS, FAILURE\nfrom twisted.internet import error\nfrom twisted.internet.protocol import ProcessProtocol\n\nclass MasterShellCommand(BuildStep):\n    \"\"\"\n    Run a shell command locally - on the buildmaster.  The shell command\n    COMMAND is specified just as for a RemoteShellCommand.  Note that extra\n    logfiles are not supported.\n    \"\"\"\n    name='MasterShellCommand'\n    description='Running'\n    descriptionDone='Ran'\n    descriptionSuffix = None\n    renderables = [ 'command', 'env', 'description', 'descriptionDone', 'descriptionSuffix' ]\n    haltOnFailure = True\n    flunkOnFailure = True\n\n    def __init__(self, command,\n                 description=None, descriptionDone=None, descriptionSuffix=None,\n                 env=None, path=None, usePTY=0, interruptSignal=\"KILL\",\n                 **kwargs):\n        BuildStep.__init__(self, **kwargs)\n\n        self.command=command\n        if description:\n            self.description = description\n        if isinstance(self.description, str):\n            self.description = [self.description]\n        if descriptionDone:\n            self.descriptionDone = descriptionDone\n        if isinstance(self.descriptionDone, str):\n            self.descriptionDone = [self.descriptionDone]\n        if descriptionSuffix:\n            self.descriptionSuffix = descriptionSuffix\n        if isinstance(self.descriptionSuffix, str):\n            self.descriptionSuffix = [self.descriptionSuffix]\n        self.env=env\n        self.path=path\n        self.usePTY=usePTY\n        self.interruptSignal = interruptSignal\n\n    class LocalPP(ProcessProtocol):\n        def __init__(self, step):\n            self.step = step\n\n        def outReceived(self, data):\n            self.step.stdio_log.addStdout(data)\n\n        def errReceived(self, data):\n            self.step.stdio_log.addStderr(data)\n\n        def processEnded(self, status_object):\n            if status_object.value.exitCode is not None:\n                self.step.stdio_log.addHeader(\"exit status %d\\n\" % status_object.value.exitCode)\n            if status_object.value.signal is not None:\n                self.step.stdio_log.addHeader(\"signal %s\\n\" % status_object.value.signal)\n            self.step.processEnded(status_object)\n\n    def start(self):\n        # render properties\n        command = self.command\n        # set up argv\n        if type(command) in types.StringTypes:\n            if runtime.platformType  == 'win32':\n                argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args\n                if '/c' not in argv: argv += ['/c']\n                argv += [command]\n            else:\n                # for posix, use /bin/sh. for other non-posix, well, doesn't\n                # hurt to try\n                argv = ['/bin/sh', '-c', command]\n        else:\n            if runtime.platformType  == 'win32':\n                argv = os.environ['COMSPEC'].split() # allow %COMSPEC% to have args\n                if '/c' not in argv: argv += ['/c']\n                argv += list(command)\n            else:\n                argv = command\n\n        self.stdio_log = stdio_log = self.addLog(\"stdio\")\n\n        if type(command) in types.StringTypes:\n            stdio_log.addHeader(command.strip() + \"\\n\\n\")\n        else:\n            stdio_log.addHeader(\" \".join(command) + \"\\n\\n\")\n        stdio_log.addHeader(\"** RUNNING ON BUILDMASTER **\\n\")\n        stdio_log.addHeader(\" in dir %s\\n\" % os.getcwd())\n        stdio_log.addHeader(\" argv: %s\\n\" % (argv,))\n        self.step_status.setText(self.describe())\n\n        if self.env is None:\n            env = os.environ\n        else:\n            assert isinstance(self.env, dict)\n            env = self.env\n\n            # do substitution on variable values matching pattern: ${name}\n            p = re.compile('\\${([0-9a-zA-Z_]*)}')\n            def subst(match):\n                return os.environ.get(match.group(1), \"\")\n            newenv = {}\n            for key in env.keys():\n                if env[key] is not None:\n                    newenv[key] = p.sub(subst, env[key])\n            env = newenv\n        stdio_log.addHeader(\" env: %r\\n\" % (env,))\n\n        # TODO add a timeout?\n        self.process = reactor.spawnProcess(self.LocalPP(self), argv[0], argv,\n                path=self.path, usePTY=self.usePTY, env=env )\n        # (the LocalPP object will call processEnded for us)\n\n    def processEnded(self, status_object):\n        if status_object.value.signal is not None:\n            self.descriptionDone = [\"killed (%s)\" % status_object.value.signal]\n            self.step_status.setText(self.describe(done=True))\n            self.finished(FAILURE)\n        elif status_object.value.exitCode != 0:\n            self.descriptionDone = [\"failed (%d)\" % status_object.value.exitCode]\n            self.step_status.setText(self.describe(done=True))\n            self.finished(FAILURE)\n        else:\n            self.step_status.setText(self.describe(done=True))\n            self.finished(SUCCESS)\n\n    def describe(self, done=False):\n        desc = self.descriptionDone if done else self.description\n        if self.descriptionSuffix:\n            desc = desc[:]\n            desc.extend(self.descriptionSuffix)\n        return desc\n\n    def interrupt(self, reason):\n        try:\n            self.process.signalProcess(self.interruptSignal)\n        except KeyError: # Process not started yet\n            pass\n        except error.ProcessExitedAlready:\n            pass\n        BuildStep.interrupt(self, reason)\n", "path": "master/buildbot/steps/master.py"}]}