We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
buildbot.tac - use an 0o prefix for octal integers
After fresh installation of buildbot workers on Rocky9 with python 3.9 we found the following problem starting the workers:

```
worker not running
Unhandled Error
Traceback (most recent call last):
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py", line 674, in run
    runApp(config)
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/scripts/twistd.py", line 29, in runApp
    runner.run()
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py", line 370, in run
    self.application = self.createOrGetApplication()
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py", line 437, in createOrGetApplication
    application = getApplication(self.config, passphrase)
--- <exception caught here> ---
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py", line 446, in getApplication
    application = service.loadApplication(filename, style, passphrase)
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/application/service.py", line 404, in loadApplication
    application = sob.loadValueFromFile(filename, "application")
  File "/home/bb-slave/lib/python3.9/site-packages/twisted/persisted/sob.py", line 176, in loadValueFromFile
    codeObj = compile(data, filename, "exec")
builtins.SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (buildbot.tac, line 32)


Failed to load application: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (buildbot.tac, line 32)
```
it turns out that the umask line in the generated buildbot.tac file causes the trouble:

```
[moko:/home/bb-slave] cat moko_linux_gtk3_slave/buildbot.tac
...
umask = 022
...
```
after changing the line, the worker starts and runs without problems:

```
umask = 0o022
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `worker/buildbot_worker/scripts/runner.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 # N.B.: don't import anything that might pull in a reactor yet. Some of our
17 # subcommands want to load modules that need the gtk reactor.
18 
19 from __future__ import absolute_import
20 from __future__ import division
21 from __future__ import print_function
22 
23 import os
24 import re
25 import sys
26 import textwrap
27 
28 from twisted.python import log
29 from twisted.python import reflect
30 from twisted.python import usage
31 
32 # the create/start/stop commands should all be run as the same user,
33 # preferably a separate 'buildbot' account.
34 
35 # Note that the terms 'options' and 'config' are used interchangeably here - in
36 # fact, they are interchanged several times.  Caveat legator.
37 
38 
39 class MakerBase(usage.Options):
40     optFlags = [
41         ['help', 'h', "Display this message"],
42         ["quiet", "q", "Do not emit the commands being run"],
43     ]
44 
45     longdesc = textwrap.dedent("""
46     Operates upon the specified <basedir> (or the current directory, if not
47     specified).
48     """)
49 
50     # on tab completion, suggest directories as first argument
51     if hasattr(usage, 'Completions'):
52         # only set completion suggestion if running with
53         # twisted version (>=11.1.0) that supports it
54         compData = usage.Completions(
55             extraActions=[usage.CompleteDirs(descr="worker base directory")])
56 
57     opt_h = usage.Options.opt_help
58 
59     def parseArgs(self, *args):
60         if args:
61             self['basedir'] = args[0]
62         else:
63             # Use the current directory if no basedir was specified.
64             self['basedir'] = os.getcwd()
65         if len(args) > 1:
66             raise usage.UsageError("I wasn't expecting so many arguments")
67 
68     def postOptions(self):
69         self['basedir'] = os.path.abspath(self['basedir'])
70 
71 
72 class StartOptions(MakerBase):
73     subcommandFunction = "buildbot_worker.scripts.start.startCommand"
74     optFlags = [
75         ['quiet', 'q', "Don't display startup log messages"],
76         ['nodaemon', None, "Don't daemonize (stay in foreground)"],
77     ]
78 
79     def getSynopsis(self):
80         return "Usage:    buildbot-worker start [<basedir>]"
81 
82 
83 class StopOptions(MakerBase):
84     subcommandFunction = "buildbot_worker.scripts.stop.stop"
85 
86     def getSynopsis(self):
87         return "Usage:    buildbot-worker stop [<basedir>]"
88 
89 
90 class RestartOptions(MakerBase):
91     subcommandFunction = "buildbot_worker.scripts.restart.restart"
92     optFlags = [
93         ['quiet', 'q', "Don't display startup log messages"],
94         ['nodaemon', None, "Don't daemonize (stay in foreground)"],
95     ]
96 
97     def getSynopsis(self):
98         return "Usage:    buildbot-worker restart [<basedir>]"
99 
100 
101 class CreateWorkerOptions(MakerBase):
102     subcommandFunction = "buildbot_worker.scripts.create_worker.createWorker"
103     optFlags = [
104         ["force", "f", "Re-use an existing directory"],
105         ["relocatable", "r",
106          "Create a relocatable buildbot.tac"],
107         ["no-logrotate", "n",
108          "Do not permit buildmaster rotate logs by itself"],
109         ['use-tls', None,
110          "Uses TLS to connect to master"],
111         ['delete-leftover-dirs', None,
112          'Delete folders that are not required by the master on connection'],
113     ]
114     optParameters = [
115         ["keepalive", "k", 600,
116          "Interval at which keepalives should be sent (in seconds)"],
117         ["umask", None, "None",
118          "controls permissions of generated files. "
119          "Use --umask=0o22 to be world-readable"],
120         ["maxdelay", None, 300,
121          "Maximum time between connection attempts"],
122         ["maxretries", None, 'None',
123          "Maximum number of retries before worker shutdown"],
124         ["numcpus", None, "None",
125          "Number of available cpus to use on a build. "],
126         ["log-size", "s", "10000000",
127          "size at which to rotate twisted log files"],
128         ["log-count", "l", "10",
129          "limit the number of kept old twisted log files "
130          "(None for unlimited)"],
131         ["allow-shutdown", "a", None,
132          "Allows the worker to initiate a graceful shutdown. One of "
133          "'signal' or 'file'"],
134         ["protocol", None, "pb", "Protocol to be used when creating master-worker connection"],
135         ["proxy-connection-string", None, None,
136          "Address of HTTP proxy to tunnel through"]
137     ]
138 
139     longdesc = textwrap.dedent("""
140     This command creates a buildbot worker directory and buildbot.tac
141     file. The bot will use the <name> and <passwd> arguments to authenticate
142     itself when connecting to the master. All commands are run in a
143     build-specific subdirectory of <basedir>. <master> is a string of the
144     form 'hostname[:port]', and specifies where the buildmaster can be reached.
145     port defaults to 9989.
146 
147     The appropriate values for <name>, <passwd>, and <master> should be
148     provided to you by the buildmaster administrator. You must choose <basedir>
149     yourself.
150     """)
151 
152     def validateMasterArgument(self, master_arg):
153         """
154         Parse the <master> argument.
155 
156         @param master_arg: the <master> argument to parse
157 
158         @return: tuple of master's host and port
159         @raise UsageError: on errors parsing the argument
160         """
161         if master_arg[:5] == "http:":
162             raise usage.UsageError("<master> is not a URL - do not use URL")
163 
164         if master_arg.startswith("[") and "]" in master_arg:
165             # detect ipv6 address with format [2001:1:2:3:4::1]:4321
166             master, port_tmp = master_arg.split("]")
167             master = master[1:]
168             if ":" not in port_tmp:
169                 port = 9989
170             else:
171                 port = port_tmp.split(":")[1]
172 
173         elif ":" not in master_arg:
174             master = master_arg
175             port = 9989
176         else:
177             try:
178                 master, port = master_arg.split(":")
179             except ValueError:
180                 raise usage.UsageError(("invalid <master> argument '{}', "
181                                         "if it is an ipv6 address, it must be enclosed by []"
182                                         ).format(master_arg))
183 
184         if not master:
185             raise usage.UsageError("invalid <master> argument '{}'".format(
186                                    master_arg))
187         try:
188             port = int(port)
189         except ValueError:
190             raise usage.UsageError("invalid master port '{}', "
191                                    "needs to be a number".format(port))
192 
193         return master, port
194 
195     def getSynopsis(self):
196         return "Usage:    buildbot-worker create-worker " \
197             "[options] <basedir> <master> <name> <passwd>"
198 
199     def parseArgs(self, *args):
200         if len(args) != 4:
201             raise usage.UsageError("incorrect number of arguments")
202         basedir, master, name, passwd = args
203         self['basedir'] = basedir
204         self['host'], self['port'] = self.validateMasterArgument(master)
205         self['name'] = name
206         self['passwd'] = passwd
207 
208     def postOptions(self):
209         MakerBase.postOptions(self)
210 
211         # check and convert numeric parameters
212         for argument in ["keepalive", "maxdelay", "log-size"]:
213             try:
214                 self[argument] = int(self[argument])
215             except ValueError:
216                 raise usage.UsageError("{} parameter needs to be a number".format(
217                                        argument))
218 
219         for argument in ["log-count", "maxretries", "umask", "numcpus"]:
220             if not re.match(r'^(0o)?\d+$', self[argument]) and \
221                     self[argument] != 'None':
222                 raise usage.UsageError("{} parameter needs to be a number"
223                                     " or None".format(argument))
224 
225         if self['allow-shutdown'] not in [None, 'signal', 'file']:
226             raise usage.UsageError("allow-shutdown needs to be one of"
227                                    " 'signal' or 'file'")
228 
229 
230 class Options(usage.Options):
231     synopsis = "Usage:    buildbot-worker <command> [command options]"
232 
233     subCommands = [
234         # the following are all admin commands
235         ['create-worker', None, CreateWorkerOptions,
236          "Create and populate a directory for a new worker"],
237         ['start', None, StartOptions, "Start a worker"],
238         ['stop', None, StopOptions, "Stop a worker"],
239         ['restart', None, RestartOptions,
240          "Restart a worker"],
241     ]
242 
243     def opt_version(self):
244         import buildbot_worker  # pylint: disable=import-outside-toplevel
245         print("worker version: {}".format(buildbot_worker.version))
246         usage.Options.opt_version(self)
247 
248     def opt_verbose(self):
249         log.startLogging(sys.stderr)
250 
251     def postOptions(self):
252         if not hasattr(self, 'subOptions'):
253             raise usage.UsageError("must specify a command")
254 
255 
256 def run():
257     config = Options()
258     try:
259         config.parseOptions()
260     except usage.error as e:
261         print("{}:  {}".format(sys.argv[0], e))
262         print()
263         c = getattr(config, 'subOptions', config)
264         print(str(c))
265         sys.exit(1)
266 
267     subconfig = config.subOptions
268     subcommandFunction = reflect.namedObject(subconfig.subcommandFunction)
269     sys.exit(subcommandFunction(subconfig))
270
```
--- 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/worker/buildbot_worker/scripts/runner.py b/worker/buildbot_worker/scripts/runner.py
--- a/worker/buildbot_worker/scripts/runner.py
+++ b/worker/buildbot_worker/scripts/runner.py
@@ -217,7 +217,7 @@
                                        argument))
 
         for argument in ["log-count", "maxretries", "umask", "numcpus"]:
-            if not re.match(r'^(0o)?\d+$', self[argument]) and \
+            if not re.match(r'^((0o)\d+|0|[1-9]\d*)$', self[argument]) and \
                     self[argument] != 'None':
                 raise usage.UsageError("{} parameter needs to be a number"
                                     " or None".format(argument))

{"golden_diff": "diff --git a/worker/buildbot_worker/scripts/runner.py b/worker/buildbot_worker/scripts/runner.py\n--- a/worker/buildbot_worker/scripts/runner.py\n+++ b/worker/buildbot_worker/scripts/runner.py\n@@ -217,7 +217,7 @@\n                                        argument))\n \n         for argument in [\"log-count\", \"maxretries\", \"umask\", \"numcpus\"]:\n-            if not re.match(r'^(0o)?\\d+$', self[argument]) and \\\n+            if not re.match(r'^((0o)\\d+|0|[1-9]\\d*)$', self[argument]) and \\\n                     self[argument] != 'None':\n                 raise usage.UsageError(\"{} parameter needs to be a number\"\n                                     \" or None\".format(argument))\n", "issue": "buildbot.tac - use an 0o prefix for octal integers\nAfter fresh installation of buildbot workers on Rocky9 with python 3.9 we found the following problem starting the workers:\r\n\r\n```\r\nworker not running\r\nUnhandled Error\r\nTraceback (most recent call last):\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py\", line 674, in run\r\n    runApp(config)\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/scripts/twistd.py\", line 29, in runApp\r\n    runner.run()\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py\", line 370, in run\r\n    self.application = self.createOrGetApplication()\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py\", line 437, in createOrGetApplication\r\n    application = getApplication(self.config, passphrase)\r\n--- <exception caught here> ---\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/application/app.py\", line 446, in getApplication\r\n    application = service.loadApplication(filename, style, passphrase)\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/application/service.py\", line 404, in loadApplication\r\n    application = sob.loadValueFromFile(filename, \"application\")\r\n  File \"/home/bb-slave/lib/python3.9/site-packages/twisted/persisted/sob.py\", line 176, in loadValueFromFile\r\n    codeObj = compile(data, filename, \"exec\")\r\nbuiltins.SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (buildbot.tac, line 32)\r\n\r\n\r\nFailed to load application: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (buildbot.tac, line 32)\r\n```\r\nit turns out that the umask line in the generated buildbot.tac file causes the trouble:\r\n\r\n```\r\n[moko:/home/bb-slave] cat moko_linux_gtk3_slave/buildbot.tac\r\n...\r\numask = 022\r\n...\r\n```\r\nafter changing the line, the worker starts and runs without problems:\r\n\r\n```\r\numask = 0o022\r\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\n# N.B.: don't import anything that might pull in a reactor yet. Some of our\n# subcommands want to load modules that need the gtk reactor.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport re\nimport sys\nimport textwrap\n\nfrom twisted.python import log\nfrom twisted.python import reflect\nfrom twisted.python import usage\n\n# the create/start/stop commands should all be run as the same user,\n# preferably a separate 'buildbot' account.\n\n# Note that the terms 'options' and 'config' are used interchangeably here - in\n# fact, they are interchanged several times.  Caveat legator.\n\n\nclass MakerBase(usage.Options):\n    optFlags = [\n        ['help', 'h', \"Display this message\"],\n        [\"quiet\", \"q\", \"Do not emit the commands being run\"],\n    ]\n\n    longdesc = textwrap.dedent(\"\"\"\n    Operates upon the specified <basedir> (or the current directory, if not\n    specified).\n    \"\"\")\n\n    # on tab completion, suggest directories as first argument\n    if hasattr(usage, 'Completions'):\n        # only set completion suggestion if running with\n        # twisted version (>=11.1.0) that supports it\n        compData = usage.Completions(\n            extraActions=[usage.CompleteDirs(descr=\"worker base directory\")])\n\n    opt_h = usage.Options.opt_help\n\n    def parseArgs(self, *args):\n        if args:\n            self['basedir'] = args[0]\n        else:\n            # Use the current directory if no basedir was specified.\n            self['basedir'] = os.getcwd()\n        if len(args) > 1:\n            raise usage.UsageError(\"I wasn't expecting so many arguments\")\n\n    def postOptions(self):\n        self['basedir'] = os.path.abspath(self['basedir'])\n\n\nclass StartOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.start.startCommand\"\n    optFlags = [\n        ['quiet', 'q', \"Don't display startup log messages\"],\n        ['nodaemon', None, \"Don't daemonize (stay in foreground)\"],\n    ]\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker start [<basedir>]\"\n\n\nclass StopOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.stop.stop\"\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker stop [<basedir>]\"\n\n\nclass RestartOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.restart.restart\"\n    optFlags = [\n        ['quiet', 'q', \"Don't display startup log messages\"],\n        ['nodaemon', None, \"Don't daemonize (stay in foreground)\"],\n    ]\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker restart [<basedir>]\"\n\n\nclass CreateWorkerOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.create_worker.createWorker\"\n    optFlags = [\n        [\"force\", \"f\", \"Re-use an existing directory\"],\n        [\"relocatable\", \"r\",\n         \"Create a relocatable buildbot.tac\"],\n        [\"no-logrotate\", \"n\",\n         \"Do not permit buildmaster rotate logs by itself\"],\n        ['use-tls', None,\n         \"Uses TLS to connect to master\"],\n        ['delete-leftover-dirs', None,\n         'Delete folders that are not required by the master on connection'],\n    ]\n    optParameters = [\n        [\"keepalive\", \"k\", 600,\n         \"Interval at which keepalives should be sent (in seconds)\"],\n        [\"umask\", None, \"None\",\n         \"controls permissions of generated files. \"\n         \"Use --umask=0o22 to be world-readable\"],\n        [\"maxdelay\", None, 300,\n         \"Maximum time between connection attempts\"],\n        [\"maxretries\", None, 'None',\n         \"Maximum number of retries before worker shutdown\"],\n        [\"numcpus\", None, \"None\",\n         \"Number of available cpus to use on a build. \"],\n        [\"log-size\", \"s\", \"10000000\",\n         \"size at which to rotate twisted log files\"],\n        [\"log-count\", \"l\", \"10\",\n         \"limit the number of kept old twisted log files \"\n         \"(None for unlimited)\"],\n        [\"allow-shutdown\", \"a\", None,\n         \"Allows the worker to initiate a graceful shutdown. One of \"\n         \"'signal' or 'file'\"],\n        [\"protocol\", None, \"pb\", \"Protocol to be used when creating master-worker connection\"],\n        [\"proxy-connection-string\", None, None,\n         \"Address of HTTP proxy to tunnel through\"]\n    ]\n\n    longdesc = textwrap.dedent(\"\"\"\n    This command creates a buildbot worker directory and buildbot.tac\n    file. The bot will use the <name> and <passwd> arguments to authenticate\n    itself when connecting to the master. All commands are run in a\n    build-specific subdirectory of <basedir>. <master> is a string of the\n    form 'hostname[:port]', and specifies where the buildmaster can be reached.\n    port defaults to 9989.\n\n    The appropriate values for <name>, <passwd>, and <master> should be\n    provided to you by the buildmaster administrator. You must choose <basedir>\n    yourself.\n    \"\"\")\n\n    def validateMasterArgument(self, master_arg):\n        \"\"\"\n        Parse the <master> argument.\n\n        @param master_arg: the <master> argument to parse\n\n        @return: tuple of master's host and port\n        @raise UsageError: on errors parsing the argument\n        \"\"\"\n        if master_arg[:5] == \"http:\":\n            raise usage.UsageError(\"<master> is not a URL - do not use URL\")\n\n        if master_arg.startswith(\"[\") and \"]\" in master_arg:\n            # detect ipv6 address with format [2001:1:2:3:4::1]:4321\n            master, port_tmp = master_arg.split(\"]\")\n            master = master[1:]\n            if \":\" not in port_tmp:\n                port = 9989\n            else:\n                port = port_tmp.split(\":\")[1]\n\n        elif \":\" not in master_arg:\n            master = master_arg\n            port = 9989\n        else:\n            try:\n                master, port = master_arg.split(\":\")\n            except ValueError:\n                raise usage.UsageError((\"invalid <master> argument '{}', \"\n                                        \"if it is an ipv6 address, it must be enclosed by []\"\n                                        ).format(master_arg))\n\n        if not master:\n            raise usage.UsageError(\"invalid <master> argument '{}'\".format(\n                                   master_arg))\n        try:\n            port = int(port)\n        except ValueError:\n            raise usage.UsageError(\"invalid master port '{}', \"\n                                   \"needs to be a number\".format(port))\n\n        return master, port\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker create-worker \" \\\n            \"[options] <basedir> <master> <name> <passwd>\"\n\n    def parseArgs(self, *args):\n        if len(args) != 4:\n            raise usage.UsageError(\"incorrect number of arguments\")\n        basedir, master, name, passwd = args\n        self['basedir'] = basedir\n        self['host'], self['port'] = self.validateMasterArgument(master)\n        self['name'] = name\n        self['passwd'] = passwd\n\n    def postOptions(self):\n        MakerBase.postOptions(self)\n\n        # check and convert numeric parameters\n        for argument in [\"keepalive\", \"maxdelay\", \"log-size\"]:\n            try:\n                self[argument] = int(self[argument])\n            except ValueError:\n                raise usage.UsageError(\"{} parameter needs to be a number\".format(\n                                       argument))\n\n        for argument in [\"log-count\", \"maxretries\", \"umask\", \"numcpus\"]:\n            if not re.match(r'^(0o)?\\d+$', self[argument]) and \\\n                    self[argument] != 'None':\n                raise usage.UsageError(\"{} parameter needs to be a number\"\n                                    \" or None\".format(argument))\n\n        if self['allow-shutdown'] not in [None, 'signal', 'file']:\n            raise usage.UsageError(\"allow-shutdown needs to be one of\"\n                                   \" 'signal' or 'file'\")\n\n\nclass Options(usage.Options):\n    synopsis = \"Usage:    buildbot-worker <command> [command options]\"\n\n    subCommands = [\n        # the following are all admin commands\n        ['create-worker', None, CreateWorkerOptions,\n         \"Create and populate a directory for a new worker\"],\n        ['start', None, StartOptions, \"Start a worker\"],\n        ['stop', None, StopOptions, \"Stop a worker\"],\n        ['restart', None, RestartOptions,\n         \"Restart a worker\"],\n    ]\n\n    def opt_version(self):\n        import buildbot_worker  # pylint: disable=import-outside-toplevel\n        print(\"worker version: {}\".format(buildbot_worker.version))\n        usage.Options.opt_version(self)\n\n    def opt_verbose(self):\n        log.startLogging(sys.stderr)\n\n    def postOptions(self):\n        if not hasattr(self, 'subOptions'):\n            raise usage.UsageError(\"must specify a command\")\n\n\ndef run():\n    config = Options()\n    try:\n        config.parseOptions()\n    except usage.error as e:\n        print(\"{}:  {}\".format(sys.argv[0], e))\n        print()\n        c = getattr(config, 'subOptions', config)\n        print(str(c))\n        sys.exit(1)\n\n    subconfig = config.subOptions\n    subcommandFunction = reflect.namedObject(subconfig.subcommandFunction)\n    sys.exit(subcommandFunction(subconfig))\n", "path": "worker/buildbot_worker/scripts/runner.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\n# N.B.: don't import anything that might pull in a reactor yet. Some of our\n# subcommands want to load modules that need the gtk reactor.\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\n\nimport os\nimport re\nimport sys\nimport textwrap\n\nfrom twisted.python import log\nfrom twisted.python import reflect\nfrom twisted.python import usage\n\n# the create/start/stop commands should all be run as the same user,\n# preferably a separate 'buildbot' account.\n\n# Note that the terms 'options' and 'config' are used interchangeably here - in\n# fact, they are interchanged several times.  Caveat legator.\n\n\nclass MakerBase(usage.Options):\n    optFlags = [\n        ['help', 'h', \"Display this message\"],\n        [\"quiet\", \"q\", \"Do not emit the commands being run\"],\n    ]\n\n    longdesc = textwrap.dedent(\"\"\"\n    Operates upon the specified <basedir> (or the current directory, if not\n    specified).\n    \"\"\")\n\n    # on tab completion, suggest directories as first argument\n    if hasattr(usage, 'Completions'):\n        # only set completion suggestion if running with\n        # twisted version (>=11.1.0) that supports it\n        compData = usage.Completions(\n            extraActions=[usage.CompleteDirs(descr=\"worker base directory\")])\n\n    opt_h = usage.Options.opt_help\n\n    def parseArgs(self, *args):\n        if args:\n            self['basedir'] = args[0]\n        else:\n            # Use the current directory if no basedir was specified.\n            self['basedir'] = os.getcwd()\n        if len(args) > 1:\n            raise usage.UsageError(\"I wasn't expecting so many arguments\")\n\n    def postOptions(self):\n        self['basedir'] = os.path.abspath(self['basedir'])\n\n\nclass StartOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.start.startCommand\"\n    optFlags = [\n        ['quiet', 'q', \"Don't display startup log messages\"],\n        ['nodaemon', None, \"Don't daemonize (stay in foreground)\"],\n    ]\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker start [<basedir>]\"\n\n\nclass StopOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.stop.stop\"\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker stop [<basedir>]\"\n\n\nclass RestartOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.restart.restart\"\n    optFlags = [\n        ['quiet', 'q', \"Don't display startup log messages\"],\n        ['nodaemon', None, \"Don't daemonize (stay in foreground)\"],\n    ]\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker restart [<basedir>]\"\n\n\nclass CreateWorkerOptions(MakerBase):\n    subcommandFunction = \"buildbot_worker.scripts.create_worker.createWorker\"\n    optFlags = [\n        [\"force\", \"f\", \"Re-use an existing directory\"],\n        [\"relocatable\", \"r\",\n         \"Create a relocatable buildbot.tac\"],\n        [\"no-logrotate\", \"n\",\n         \"Do not permit buildmaster rotate logs by itself\"],\n        ['use-tls', None,\n         \"Uses TLS to connect to master\"],\n        ['delete-leftover-dirs', None,\n         'Delete folders that are not required by the master on connection'],\n    ]\n    optParameters = [\n        [\"keepalive\", \"k\", 600,\n         \"Interval at which keepalives should be sent (in seconds)\"],\n        [\"umask\", None, \"None\",\n         \"controls permissions of generated files. \"\n         \"Use --umask=0o22 to be world-readable\"],\n        [\"maxdelay\", None, 300,\n         \"Maximum time between connection attempts\"],\n        [\"maxretries\", None, 'None',\n         \"Maximum number of retries before worker shutdown\"],\n        [\"numcpus\", None, \"None\",\n         \"Number of available cpus to use on a build. \"],\n        [\"log-size\", \"s\", \"10000000\",\n         \"size at which to rotate twisted log files\"],\n        [\"log-count\", \"l\", \"10\",\n         \"limit the number of kept old twisted log files \"\n         \"(None for unlimited)\"],\n        [\"allow-shutdown\", \"a\", None,\n         \"Allows the worker to initiate a graceful shutdown. One of \"\n         \"'signal' or 'file'\"],\n        [\"protocol\", None, \"pb\", \"Protocol to be used when creating master-worker connection\"],\n        [\"proxy-connection-string\", None, None,\n         \"Address of HTTP proxy to tunnel through\"]\n    ]\n\n    longdesc = textwrap.dedent(\"\"\"\n    This command creates a buildbot worker directory and buildbot.tac\n    file. The bot will use the <name> and <passwd> arguments to authenticate\n    itself when connecting to the master. All commands are run in a\n    build-specific subdirectory of <basedir>. <master> is a string of the\n    form 'hostname[:port]', and specifies where the buildmaster can be reached.\n    port defaults to 9989.\n\n    The appropriate values for <name>, <passwd>, and <master> should be\n    provided to you by the buildmaster administrator. You must choose <basedir>\n    yourself.\n    \"\"\")\n\n    def validateMasterArgument(self, master_arg):\n        \"\"\"\n        Parse the <master> argument.\n\n        @param master_arg: the <master> argument to parse\n\n        @return: tuple of master's host and port\n        @raise UsageError: on errors parsing the argument\n        \"\"\"\n        if master_arg[:5] == \"http:\":\n            raise usage.UsageError(\"<master> is not a URL - do not use URL\")\n\n        if master_arg.startswith(\"[\") and \"]\" in master_arg:\n            # detect ipv6 address with format [2001:1:2:3:4::1]:4321\n            master, port_tmp = master_arg.split(\"]\")\n            master = master[1:]\n            if \":\" not in port_tmp:\n                port = 9989\n            else:\n                port = port_tmp.split(\":\")[1]\n\n        elif \":\" not in master_arg:\n            master = master_arg\n            port = 9989\n        else:\n            try:\n                master, port = master_arg.split(\":\")\n            except ValueError:\n                raise usage.UsageError((\"invalid <master> argument '{}', \"\n                                        \"if it is an ipv6 address, it must be enclosed by []\"\n                                        ).format(master_arg))\n\n        if not master:\n            raise usage.UsageError(\"invalid <master> argument '{}'\".format(\n                                   master_arg))\n        try:\n            port = int(port)\n        except ValueError:\n            raise usage.UsageError(\"invalid master port '{}', \"\n                                   \"needs to be a number\".format(port))\n\n        return master, port\n\n    def getSynopsis(self):\n        return \"Usage:    buildbot-worker create-worker \" \\\n            \"[options] <basedir> <master> <name> <passwd>\"\n\n    def parseArgs(self, *args):\n        if len(args) != 4:\n            raise usage.UsageError(\"incorrect number of arguments\")\n        basedir, master, name, passwd = args\n        self['basedir'] = basedir\n        self['host'], self['port'] = self.validateMasterArgument(master)\n        self['name'] = name\n        self['passwd'] = passwd\n\n    def postOptions(self):\n        MakerBase.postOptions(self)\n\n        # check and convert numeric parameters\n        for argument in [\"keepalive\", \"maxdelay\", \"log-size\"]:\n            try:\n                self[argument] = int(self[argument])\n            except ValueError:\n                raise usage.UsageError(\"{} parameter needs to be a number\".format(\n                                       argument))\n\n        for argument in [\"log-count\", \"maxretries\", \"umask\", \"numcpus\"]:\n            if not re.match(r'^((0o)\\d+|0|[1-9]\\d*)$', self[argument]) and \\\n                    self[argument] != 'None':\n                raise usage.UsageError(\"{} parameter needs to be a number\"\n                                    \" or None\".format(argument))\n\n        if self['allow-shutdown'] not in [None, 'signal', 'file']:\n            raise usage.UsageError(\"allow-shutdown needs to be one of\"\n                                   \" 'signal' or 'file'\")\n\n\nclass Options(usage.Options):\n    synopsis = \"Usage:    buildbot-worker <command> [command options]\"\n\n    subCommands = [\n        # the following are all admin commands\n        ['create-worker', None, CreateWorkerOptions,\n         \"Create and populate a directory for a new worker\"],\n        ['start', None, StartOptions, \"Start a worker\"],\n        ['stop', None, StopOptions, \"Stop a worker\"],\n        ['restart', None, RestartOptions,\n         \"Restart a worker\"],\n    ]\n\n    def opt_version(self):\n        import buildbot_worker  # pylint: disable=import-outside-toplevel\n        print(\"worker version: {}\".format(buildbot_worker.version))\n        usage.Options.opt_version(self)\n\n    def opt_verbose(self):\n        log.startLogging(sys.stderr)\n\n    def postOptions(self):\n        if not hasattr(self, 'subOptions'):\n            raise usage.UsageError(\"must specify a command\")\n\n\ndef run():\n    config = Options()\n    try:\n        config.parseOptions()\n    except usage.error as e:\n        print(\"{}:  {}\".format(sys.argv[0], e))\n        print()\n        c = getattr(config, 'subOptions', config)\n        print(str(c))\n        sys.exit(1)\n\n    subconfig = config.subOptions\n    subcommandFunction = reflect.namedObject(subconfig.subcommandFunction)\n    sys.exit(subcommandFunction(subconfig))\n", "path": "worker/buildbot_worker/scripts/runner.py"}]}