We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
in local development, zip uploads are broken
```
|   File "/build/clusterfuzz/src/appengine/handlers/jobs.py", line 132, in post
|     blob_info = self.get_upload()
|   File "/build/clusterfuzz/src/appengine/handlers/base_handler.py", line 317, in get_upload
|     blob_info = storage.GcsBlobInfo.from_key(upload_key)
|   File "/build/clusterfuzz/src/python/google_cloud_utils/storage.py", line 556, in from_key
|     logs.log_error('Failed to get blob from key %s.' % key)
| LogError: Failed to get blob from key 0e3179ad-31b4-4ba5-a45c-86a610e065c7.
| Traceback (most recent call last):
|   File "/build/clusterfuzz/src/python/google_cloud_utils/storage.py", line 554, in from_key
|     return GcsBlobInfo(blobs_bucket(), key)
|   File "/build/clusterfuzz/src/python/google_cloud_utils/storage.py", line 536, in __init__
|     self.filename = gcs_object['metadata'].get(BLOB_FILENAME_METADATA_KEY)
| TypeError: 'NoneType' object is not subscriptable
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/local/butler/run_server.py`
Content:
```
1 # Copyright 2019 Google LLC
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """run_server.py run the Clusterfuzz server locally."""
15 from __future__ import print_function
16 from future import standard_library
17 standard_library.install_aliases()
18 import os
19 import shutil
20 import threading
21 import time
22 import urllib.request
23 
24 from local.butler import appengine
25 from local.butler import common
26 from local.butler import constants
27 from python.config import local_config
28 from python.tests.test_libs import test_utils
29 
30 
31 def bootstrap_db():
32   """Bootstrap the DB."""
33 
34   def bootstrap():
35     # Wait for the server to run.
36     time.sleep(10)
37     print('Bootstrapping datastore...')
38     common.execute(
39         ('python butler.py run setup '
40          '--non-dry-run --local --config-dir={config_dir}'
41         ).format(config_dir=constants.TEST_CONFIG_DIR),
42         exit_on_error=False)
43 
44   thread = threading.Thread(target=bootstrap)
45   thread.start()
46 
47 
48 def create_local_bucket(local_gcs_buckets_path, name):
49   """Create a local bucket."""
50   blobs_bucket = os.path.join(local_gcs_buckets_path, name)
51   if not os.path.exists(blobs_bucket):
52     os.mkdir(blobs_bucket)
53 
54 
55 def bootstrap_gcs(storage_path):
56   """Bootstrap GCS."""
57   local_gcs_buckets_path = os.path.join(storage_path, 'local_gcs')
58   if not os.path.exists(local_gcs_buckets_path):
59     os.mkdir(local_gcs_buckets_path)
60 
61   config = local_config.ProjectConfig()
62   create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))
63   create_local_bucket(local_gcs_buckets_path, config.get('deployment.bucket'))
64   create_local_bucket(local_gcs_buckets_path, config.get('bigquery.bucket'))
65   create_local_bucket(local_gcs_buckets_path, config.get('backup.bucket'))
66   create_local_bucket(local_gcs_buckets_path, config.get('logs.fuzzer.bucket'))
67   create_local_bucket(local_gcs_buckets_path, config.get('env.CORPUS_BUCKET'))
68   create_local_bucket(local_gcs_buckets_path,
69                       config.get('env.QUARANTINE_BUCKET'))
70   create_local_bucket(local_gcs_buckets_path,
71                       config.get('env.SHARED_CORPUS_BUCKET'))
72   create_local_bucket(local_gcs_buckets_path,
73                       config.get('env.FUZZ_LOGS_BUCKET'))
74   create_local_bucket(local_gcs_buckets_path,
75                       config.get('env.MUTATOR_PLUGINS_BUCKET'))
76 
77   # Symlink local GCS bucket path to appengine src dir to bypass sandboxing
78   # issues.
79   common.symlink(
80       src=local_gcs_buckets_path,
81       target=os.path.join(appengine.SRC_DIR_PY, 'local_gcs'))
82 
83 
84 def start_cron_threads():
85   """Start threads to trigger essential cron jobs."""
86 
87   request_timeout = 10 * 60  # 10 minutes.
88 
89   def trigger(interval_seconds, target):
90     """Trigger a cron job."""
91     while True:
92       time.sleep(interval_seconds)
93 
94       try:
95         url = 'http://{host}/{target}'.format(
96             host=constants.CRON_SERVICE_HOST, target=target)
97         request = urllib.request.Request(url)
98         request.add_header('X-Appengine-Cron', 'true')
99         response = urllib.request.urlopen(request, timeout=request_timeout)
100         response.read(60)  # wait for request to finish.
101       except Exception:
102         continue
103 
104   crons = (
105       (90, 'cleanup'),
106       (60, 'triage'),
107       (6 * 3600, 'schedule-progression-tasks'),
108       (12 * 3600, 'schedule-corpus-pruning'),
109   )
110 
111   for interval, cron in crons:
112     thread = threading.Thread(target=trigger, args=(interval, cron))
113     thread.daemon = True
114     thread.start()
115 
116 
117 def execute(args):
118   """Run the server."""
119   os.environ['LOCAL_DEVELOPMENT'] = 'True'
120   common.kill_leftover_emulators()
121 
122   if not args.skip_install_deps:
123     common.install_dependencies()
124 
125   # Do this everytime as a past deployment might have changed these.
126   appengine.symlink_dirs()
127 
128   # Deploy all yaml files from test project for basic appengine deployment and
129   # local testing to work. This needs to be called on every iteration as a past
130   # deployment might have overwritten or deleted these config files.
131   yaml_paths = local_config.GAEConfig().get_absolute_path('deployment.prod3')
132   appengine.copy_yamls_and_preprocess(yaml_paths)
133 
134   # Build templates.
135   appengine.build_templates()
136 
137   # Clean storage directory if needed.
138   if args.bootstrap or args.clean:
139     if os.path.exists(args.storage_path):
140       print('Clearing local datastore by removing %s.' % args.storage_path)
141       shutil.rmtree(args.storage_path)
142   if not os.path.exists(args.storage_path):
143     os.makedirs(args.storage_path)
144 
145   # Set up local GCS buckets and symlinks.
146   bootstrap_gcs(args.storage_path)
147 
148   # Start pubsub emulator.
149   pubsub_emulator = test_utils.start_cloud_emulator(
150       'pubsub',
151       args=['--host-port=' + constants.PUBSUB_EMULATOR_HOST],
152       data_dir=args.storage_path)
153   test_utils.setup_pubsub(constants.TEST_APP_ID)
154 
155   # Start Datastore emulator
156   datastore_emulator = test_utils.start_cloud_emulator(
157       'datastore',
158       args=['--host-port=' + constants.DATASTORE_EMULATOR_HOST],
159       data_dir=args.storage_path,
160       store_on_disk=True)
161 
162   # Start our custom GCS emulator.
163   local_gcs = common.execute_async(
164       'go run emulators/gcs.go -storage-path=' + args.storage_path, cwd='local')
165 
166   if args.bootstrap:
167     bootstrap_db()
168 
169   start_cron_threads()
170 
171   os.environ['APPLICATION_ID'] = constants.TEST_APP_ID
172   os.environ['LOCAL_DEVELOPMENT'] = 'True'
173   os.environ['LOCAL_GCS_BUCKETS_PATH'] = 'local_gcs'
174   os.environ['LOCAL_GCS_SERVER_HOST'] = constants.LOCAL_GCS_SERVER_HOST
175   os.environ['DATASTORE_EMULATOR_HOST'] = constants.DATASTORE_EMULATOR_HOST
176   os.environ['PUBSUB_EMULATOR_HOST'] = constants.PUBSUB_EMULATOR_HOST
177   os.environ['GAE_ENV'] = 'dev'
178   try:
179     cron_server = common.execute_async(
180         'gunicorn -b :{port} main:app'.format(port=constants.CRON_SERVICE_PORT),
181         cwd=os.path.join('src', 'appengine'))
182 
183     common.execute(
184         'gunicorn -b :{port} main:app'.format(
185             port=constants.DEV_APPSERVER_PORT),
186         cwd=os.path.join('src', 'appengine'))
187   except KeyboardInterrupt:
188     print('Server has been stopped. Exit.')
189     cron_server.terminate()
190     datastore_emulator.cleanup()
191     pubsub_emulator.cleanup()
192     local_gcs.terminate()
193
```
--- 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/src/local/butler/run_server.py b/src/local/butler/run_server.py
--- a/src/local/butler/run_server.py
+++ b/src/local/butler/run_server.py
@@ -59,7 +59,12 @@
     os.mkdir(local_gcs_buckets_path)
 
   config = local_config.ProjectConfig()
-  create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))
+  test_blobs_bucket = os.environ.get('TEST_BLOBS_BUCKET')
+  if test_blobs_bucket:
+    create_local_bucket(local_gcs_buckets_path, test_blobs_bucket)
+  else:
+    create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))
+
   create_local_bucket(local_gcs_buckets_path, config.get('deployment.bucket'))
   create_local_bucket(local_gcs_buckets_path, config.get('bigquery.bucket'))
   create_local_bucket(local_gcs_buckets_path, config.get('backup.bucket'))

{"golden_diff": "diff --git a/src/local/butler/run_server.py b/src/local/butler/run_server.py\n--- a/src/local/butler/run_server.py\n+++ b/src/local/butler/run_server.py\n@@ -59,7 +59,12 @@\n     os.mkdir(local_gcs_buckets_path)\n \n   config = local_config.ProjectConfig()\n-  create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))\n+  test_blobs_bucket = os.environ.get('TEST_BLOBS_BUCKET')\n+  if test_blobs_bucket:\n+    create_local_bucket(local_gcs_buckets_path, test_blobs_bucket)\n+  else:\n+    create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))\n+\n   create_local_bucket(local_gcs_buckets_path, config.get('deployment.bucket'))\n   create_local_bucket(local_gcs_buckets_path, config.get('bigquery.bucket'))\n   create_local_bucket(local_gcs_buckets_path, config.get('backup.bucket'))\n", "issue": "in local development, zip uploads are broken\n```\r\n|   File \"/build/clusterfuzz/src/appengine/handlers/jobs.py\", line 132, in post\r\n|     blob_info = self.get_upload()\r\n|   File \"/build/clusterfuzz/src/appengine/handlers/base_handler.py\", line 317, in get_upload\r\n|     blob_info = storage.GcsBlobInfo.from_key(upload_key)\r\n|   File \"/build/clusterfuzz/src/python/google_cloud_utils/storage.py\", line 556, in from_key\r\n|     logs.log_error('Failed to get blob from key %s.' % key)\r\n| LogError: Failed to get blob from key 0e3179ad-31b4-4ba5-a45c-86a610e065c7.\r\n| Traceback (most recent call last):\r\n|   File \"/build/clusterfuzz/src/python/google_cloud_utils/storage.py\", line 554, in from_key\r\n|     return GcsBlobInfo(blobs_bucket(), key)\r\n|   File \"/build/clusterfuzz/src/python/google_cloud_utils/storage.py\", line 536, in __init__\r\n|     self.filename = gcs_object['metadata'].get(BLOB_FILENAME_METADATA_KEY)\r\n| TypeError: 'NoneType' object is not subscriptable\r\n```\n", "before_files": [{"content": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"run_server.py run the Clusterfuzz server locally.\"\"\"\nfrom __future__ import print_function\nfrom future import standard_library\nstandard_library.install_aliases()\nimport os\nimport shutil\nimport threading\nimport time\nimport urllib.request\n\nfrom local.butler import appengine\nfrom local.butler import common\nfrom local.butler import constants\nfrom python.config import local_config\nfrom python.tests.test_libs import test_utils\n\n\ndef bootstrap_db():\n  \"\"\"Bootstrap the DB.\"\"\"\n\n  def bootstrap():\n    # Wait for the server to run.\n    time.sleep(10)\n    print('Bootstrapping datastore...')\n    common.execute(\n        ('python butler.py run setup '\n         '--non-dry-run --local --config-dir={config_dir}'\n        ).format(config_dir=constants.TEST_CONFIG_DIR),\n        exit_on_error=False)\n\n  thread = threading.Thread(target=bootstrap)\n  thread.start()\n\n\ndef create_local_bucket(local_gcs_buckets_path, name):\n  \"\"\"Create a local bucket.\"\"\"\n  blobs_bucket = os.path.join(local_gcs_buckets_path, name)\n  if not os.path.exists(blobs_bucket):\n    os.mkdir(blobs_bucket)\n\n\ndef bootstrap_gcs(storage_path):\n  \"\"\"Bootstrap GCS.\"\"\"\n  local_gcs_buckets_path = os.path.join(storage_path, 'local_gcs')\n  if not os.path.exists(local_gcs_buckets_path):\n    os.mkdir(local_gcs_buckets_path)\n\n  config = local_config.ProjectConfig()\n  create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('deployment.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('bigquery.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('backup.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('logs.fuzzer.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('env.CORPUS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.QUARANTINE_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.SHARED_CORPUS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.FUZZ_LOGS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.MUTATOR_PLUGINS_BUCKET'))\n\n  # Symlink local GCS bucket path to appengine src dir to bypass sandboxing\n  # issues.\n  common.symlink(\n      src=local_gcs_buckets_path,\n      target=os.path.join(appengine.SRC_DIR_PY, 'local_gcs'))\n\n\ndef start_cron_threads():\n  \"\"\"Start threads to trigger essential cron jobs.\"\"\"\n\n  request_timeout = 10 * 60  # 10 minutes.\n\n  def trigger(interval_seconds, target):\n    \"\"\"Trigger a cron job.\"\"\"\n    while True:\n      time.sleep(interval_seconds)\n\n      try:\n        url = 'http://{host}/{target}'.format(\n            host=constants.CRON_SERVICE_HOST, target=target)\n        request = urllib.request.Request(url)\n        request.add_header('X-Appengine-Cron', 'true')\n        response = urllib.request.urlopen(request, timeout=request_timeout)\n        response.read(60)  # wait for request to finish.\n      except Exception:\n        continue\n\n  crons = (\n      (90, 'cleanup'),\n      (60, 'triage'),\n      (6 * 3600, 'schedule-progression-tasks'),\n      (12 * 3600, 'schedule-corpus-pruning'),\n  )\n\n  for interval, cron in crons:\n    thread = threading.Thread(target=trigger, args=(interval, cron))\n    thread.daemon = True\n    thread.start()\n\n\ndef execute(args):\n  \"\"\"Run the server.\"\"\"\n  os.environ['LOCAL_DEVELOPMENT'] = 'True'\n  common.kill_leftover_emulators()\n\n  if not args.skip_install_deps:\n    common.install_dependencies()\n\n  # Do this everytime as a past deployment might have changed these.\n  appengine.symlink_dirs()\n\n  # Deploy all yaml files from test project for basic appengine deployment and\n  # local testing to work. This needs to be called on every iteration as a past\n  # deployment might have overwritten or deleted these config files.\n  yaml_paths = local_config.GAEConfig().get_absolute_path('deployment.prod3')\n  appengine.copy_yamls_and_preprocess(yaml_paths)\n\n  # Build templates.\n  appengine.build_templates()\n\n  # Clean storage directory if needed.\n  if args.bootstrap or args.clean:\n    if os.path.exists(args.storage_path):\n      print('Clearing local datastore by removing %s.' % args.storage_path)\n      shutil.rmtree(args.storage_path)\n  if not os.path.exists(args.storage_path):\n    os.makedirs(args.storage_path)\n\n  # Set up local GCS buckets and symlinks.\n  bootstrap_gcs(args.storage_path)\n\n  # Start pubsub emulator.\n  pubsub_emulator = test_utils.start_cloud_emulator(\n      'pubsub',\n      args=['--host-port=' + constants.PUBSUB_EMULATOR_HOST],\n      data_dir=args.storage_path)\n  test_utils.setup_pubsub(constants.TEST_APP_ID)\n\n  # Start Datastore emulator\n  datastore_emulator = test_utils.start_cloud_emulator(\n      'datastore',\n      args=['--host-port=' + constants.DATASTORE_EMULATOR_HOST],\n      data_dir=args.storage_path,\n      store_on_disk=True)\n\n  # Start our custom GCS emulator.\n  local_gcs = common.execute_async(\n      'go run emulators/gcs.go -storage-path=' + args.storage_path, cwd='local')\n\n  if args.bootstrap:\n    bootstrap_db()\n\n  start_cron_threads()\n\n  os.environ['APPLICATION_ID'] = constants.TEST_APP_ID\n  os.environ['LOCAL_DEVELOPMENT'] = 'True'\n  os.environ['LOCAL_GCS_BUCKETS_PATH'] = 'local_gcs'\n  os.environ['LOCAL_GCS_SERVER_HOST'] = constants.LOCAL_GCS_SERVER_HOST\n  os.environ['DATASTORE_EMULATOR_HOST'] = constants.DATASTORE_EMULATOR_HOST\n  os.environ['PUBSUB_EMULATOR_HOST'] = constants.PUBSUB_EMULATOR_HOST\n  os.environ['GAE_ENV'] = 'dev'\n  try:\n    cron_server = common.execute_async(\n        'gunicorn -b :{port} main:app'.format(port=constants.CRON_SERVICE_PORT),\n        cwd=os.path.join('src', 'appengine'))\n\n    common.execute(\n        'gunicorn -b :{port} main:app'.format(\n            port=constants.DEV_APPSERVER_PORT),\n        cwd=os.path.join('src', 'appengine'))\n  except KeyboardInterrupt:\n    print('Server has been stopped. Exit.')\n    cron_server.terminate()\n    datastore_emulator.cleanup()\n    pubsub_emulator.cleanup()\n    local_gcs.terminate()\n", "path": "src/local/butler/run_server.py"}], "after_files": [{"content": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"run_server.py run the Clusterfuzz server locally.\"\"\"\nfrom __future__ import print_function\nfrom future import standard_library\nstandard_library.install_aliases()\nimport os\nimport shutil\nimport threading\nimport time\nimport urllib.request\n\nfrom local.butler import appengine\nfrom local.butler import common\nfrom local.butler import constants\nfrom python.config import local_config\nfrom python.tests.test_libs import test_utils\n\n\ndef bootstrap_db():\n  \"\"\"Bootstrap the DB.\"\"\"\n\n  def bootstrap():\n    # Wait for the server to run.\n    time.sleep(10)\n    print('Bootstrapping datastore...')\n    common.execute(\n        ('python butler.py run setup '\n         '--non-dry-run --local --config-dir={config_dir}'\n        ).format(config_dir=constants.TEST_CONFIG_DIR),\n        exit_on_error=False)\n\n  thread = threading.Thread(target=bootstrap)\n  thread.start()\n\n\ndef create_local_bucket(local_gcs_buckets_path, name):\n  \"\"\"Create a local bucket.\"\"\"\n  blobs_bucket = os.path.join(local_gcs_buckets_path, name)\n  if not os.path.exists(blobs_bucket):\n    os.mkdir(blobs_bucket)\n\n\ndef bootstrap_gcs(storage_path):\n  \"\"\"Bootstrap GCS.\"\"\"\n  local_gcs_buckets_path = os.path.join(storage_path, 'local_gcs')\n  if not os.path.exists(local_gcs_buckets_path):\n    os.mkdir(local_gcs_buckets_path)\n\n  config = local_config.ProjectConfig()\n  test_blobs_bucket = os.environ.get('TEST_BLOBS_BUCKET')\n  if test_blobs_bucket:\n    create_local_bucket(local_gcs_buckets_path, test_blobs_bucket)\n  else:\n    create_local_bucket(local_gcs_buckets_path, config.get('blobs.bucket'))\n\n  create_local_bucket(local_gcs_buckets_path, config.get('deployment.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('bigquery.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('backup.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('logs.fuzzer.bucket'))\n  create_local_bucket(local_gcs_buckets_path, config.get('env.CORPUS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.QUARANTINE_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.SHARED_CORPUS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.FUZZ_LOGS_BUCKET'))\n  create_local_bucket(local_gcs_buckets_path,\n                      config.get('env.MUTATOR_PLUGINS_BUCKET'))\n\n  # Symlink local GCS bucket path to appengine src dir to bypass sandboxing\n  # issues.\n  common.symlink(\n      src=local_gcs_buckets_path,\n      target=os.path.join(appengine.SRC_DIR_PY, 'local_gcs'))\n\n\ndef start_cron_threads():\n  \"\"\"Start threads to trigger essential cron jobs.\"\"\"\n\n  request_timeout = 10 * 60  # 10 minutes.\n\n  def trigger(interval_seconds, target):\n    \"\"\"Trigger a cron job.\"\"\"\n    while True:\n      time.sleep(interval_seconds)\n\n      try:\n        url = 'http://{host}/{target}'.format(\n            host=constants.CRON_SERVICE_HOST, target=target)\n        request = urllib.request.Request(url)\n        request.add_header('X-Appengine-Cron', 'true')\n        response = urllib.request.urlopen(request, timeout=request_timeout)\n        response.read(60)  # wait for request to finish.\n      except Exception:\n        continue\n\n  crons = (\n      (90, 'cleanup'),\n      (60, 'triage'),\n      (6 * 3600, 'schedule-progression-tasks'),\n      (12 * 3600, 'schedule-corpus-pruning'),\n  )\n\n  for interval, cron in crons:\n    thread = threading.Thread(target=trigger, args=(interval, cron))\n    thread.daemon = True\n    thread.start()\n\n\ndef execute(args):\n  \"\"\"Run the server.\"\"\"\n  os.environ['LOCAL_DEVELOPMENT'] = 'True'\n  common.kill_leftover_emulators()\n\n  if not args.skip_install_deps:\n    common.install_dependencies()\n\n  # Do this everytime as a past deployment might have changed these.\n  appengine.symlink_dirs()\n\n  # Deploy all yaml files from test project for basic appengine deployment and\n  # local testing to work. This needs to be called on every iteration as a past\n  # deployment might have overwritten or deleted these config files.\n  yaml_paths = local_config.GAEConfig().get_absolute_path('deployment.prod3')\n  appengine.copy_yamls_and_preprocess(yaml_paths)\n\n  # Build templates.\n  appengine.build_templates()\n\n  # Clean storage directory if needed.\n  if args.bootstrap or args.clean:\n    if os.path.exists(args.storage_path):\n      print('Clearing local datastore by removing %s.' % args.storage_path)\n      shutil.rmtree(args.storage_path)\n  if not os.path.exists(args.storage_path):\n    os.makedirs(args.storage_path)\n\n  # Set up local GCS buckets and symlinks.\n  bootstrap_gcs(args.storage_path)\n\n  # Start pubsub emulator.\n  pubsub_emulator = test_utils.start_cloud_emulator(\n      'pubsub',\n      args=['--host-port=' + constants.PUBSUB_EMULATOR_HOST],\n      data_dir=args.storage_path)\n  test_utils.setup_pubsub(constants.TEST_APP_ID)\n\n  # Start Datastore emulator\n  datastore_emulator = test_utils.start_cloud_emulator(\n      'datastore',\n      args=['--host-port=' + constants.DATASTORE_EMULATOR_HOST],\n      data_dir=args.storage_path,\n      store_on_disk=True)\n\n  # Start our custom GCS emulator.\n  local_gcs = common.execute_async(\n      'go run emulators/gcs.go -storage-path=' + args.storage_path, cwd='local')\n\n  if args.bootstrap:\n    bootstrap_db()\n\n  start_cron_threads()\n\n  os.environ['APPLICATION_ID'] = constants.TEST_APP_ID\n  os.environ['LOCAL_DEVELOPMENT'] = 'True'\n  os.environ['LOCAL_GCS_BUCKETS_PATH'] = 'local_gcs'\n  os.environ['LOCAL_GCS_SERVER_HOST'] = constants.LOCAL_GCS_SERVER_HOST\n  os.environ['DATASTORE_EMULATOR_HOST'] = constants.DATASTORE_EMULATOR_HOST\n  os.environ['PUBSUB_EMULATOR_HOST'] = constants.PUBSUB_EMULATOR_HOST\n  os.environ['GAE_ENV'] = 'dev'\n  try:\n    cron_server = common.execute_async(\n        'gunicorn -b :{port} main:app'.format(port=constants.CRON_SERVICE_PORT),\n        cwd=os.path.join('src', 'appengine'))\n\n    common.execute(\n        'gunicorn -b :{port} main:app'.format(\n            port=constants.DEV_APPSERVER_PORT),\n        cwd=os.path.join('src', 'appengine'))\n  except KeyboardInterrupt:\n    print('Server has been stopped. Exit.')\n    cron_server.terminate()\n    datastore_emulator.cleanup()\n    pubsub_emulator.cleanup()\n    local_gcs.terminate()\n", "path": "src/local/butler/run_server.py"}]}