prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Require Celery app reference to read configuration
  We had a customer whose Celery tasks weren't reporting whilst their Django views were. It turns out they had configured in the Django settings file, which isn't applied when Celery runs. This is because it doesn't run "under" Django through `manage.py`, but separately through `celery worker`.
  
  The django pattern is to use [Celery's `app.config_from_object`](https://docs.celeryproject.org/en/latest/reference/celery.html#celery.Celery.config_from_object) to read the Django settings. If we then read out of there for the scout settings, we would again allow shared configuration between the two.
  
  This would need changing the Celery install process to take an `app` argument:
  
  ```python
  app = celery.Celery(..)
  ...
  scout_apm.celery.install(app)
  ```
  
  We should work without this for backwards compatibility reasons, but throw a warninng when it's not passed as I predict this issue will appear repeatedly if we don't encourage users this way.
  
  </issue>
  <code>
  [start of src/scout_apm/celery.py]
  1 # coding=utf-8
  2 from __future__ import absolute_import, division, print_function, unicode_literals
  3 
  4 import datetime as dt
  5 
  6 from celery.signals import before_task_publish, task_postrun, task_prerun
  7 
  8 import scout_apm.core
  9 from scout_apm.compat import datetime_to_timestamp
  10 from scout_apm.core.tracked_request import TrackedRequest
  11 
  12 
  13 def before_publish_callback(headers=None, properties=None, **kwargs):
  14     if "scout_task_start" not in headers:
  15         headers["scout_task_start"] = datetime_to_timestamp(dt.datetime.utcnow())
  16 
  17 
  18 def prerun_callback(task=None, **kwargs):
  19     tracked_request = TrackedRequest.instance()
  20     tracked_request.is_real_request = True
  21 
  22     start = getattr(task.request, "scout_task_start", None)
  23     if start is not None:
  24         now = datetime_to_timestamp(dt.datetime.utcnow())
  25         try:
  26             queue_time = now - start
  27         except TypeError:
  28             pass
  29         else:
  30             tracked_request.tag("queue_time", queue_time)
  31 
  32     task_id = getattr(task.request, "id", None)
  33     if task_id:
  34         tracked_request.tag("task_id", task_id)
  35     parent_task_id = getattr(task.request, "parent_id", None)
  36     if parent_task_id:
  37         tracked_request.tag("parent_task_id", parent_task_id)
  38 
  39     delivery_info = task.request.delivery_info
  40     tracked_request.tag("is_eager", delivery_info.get("is_eager", False))
  41     tracked_request.tag("exchange", delivery_info.get("exchange", "unknown"))
  42     tracked_request.tag("routing_key", delivery_info.get("routing_key", "unknown"))
  43     tracked_request.tag("queue", delivery_info.get("queue", "unknown"))
  44 
  45     tracked_request.start_span(operation=("Job/" + task.name))
  46 
  47 
  48 def postrun_callback(task=None, **kwargs):
  49     tracked_request = TrackedRequest.instance()
  50     tracked_request.stop_span()
  51 
  52 
  53 def install():
  54     installed = scout_apm.core.install()
  55     if not installed:
  56         return
  57 
  58     before_task_publish.connect(before_publish_callback)
  59     task_prerun.connect(prerun_callback)
  60     task_postrun.connect(postrun_callback)
  61 
  62 
  63 def uninstall():
  64     before_task_publish.disconnect(before_publish_callback)
  65     task_prerun.disconnect(prerun_callback)
  66     task_postrun.disconnect(postrun_callback)
  67
  [end of src/scout_apm/celery.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/src/scout_apm/celery.py b/src/scout_apm/celery.py
  --- a/src/scout_apm/celery.py
  +++ b/src/scout_apm/celery.py
  @@ -7,15 +7,16 @@
   
   import scout_apm.core
   from scout_apm.compat import datetime_to_timestamp
  +from scout_apm.core.config import scout_config
   from scout_apm.core.tracked_request import TrackedRequest
   
   
  -def before_publish_callback(headers=None, properties=None, **kwargs):
  +def before_task_publish_callback(headers=None, properties=None, **kwargs):
       if "scout_task_start" not in headers:
           headers["scout_task_start"] = datetime_to_timestamp(dt.datetime.utcnow())
   
   
  -def prerun_callback(task=None, **kwargs):
  +def task_prerun_callback(task=None, **kwargs):
       tracked_request = TrackedRequest.instance()
       tracked_request.is_real_request = True
   
  @@ -45,22 +46,39 @@
       tracked_request.start_span(operation=("Job/" + task.name))
   
   
  -def postrun_callback(task=None, **kwargs):
  +def task_postrun_callback(task=None, **kwargs):
       tracked_request = TrackedRequest.instance()
       tracked_request.stop_span()
   
   
  -def install():
  +def install(app=None):
  +    if app is not None:
  +        copy_configuration(app)
  +
       installed = scout_apm.core.install()
       if not installed:
           return
   
  -    before_task_publish.connect(before_publish_callback)
  -    task_prerun.connect(prerun_callback)
  -    task_postrun.connect(postrun_callback)
  +    before_task_publish.connect(before_task_publish_callback)
  +    task_prerun.connect(task_prerun_callback)
  +    task_postrun.connect(task_postrun_callback)
  +
  +
  +def copy_configuration(app):
  +    prefix = "scout_"
  +    prefix_len = len(prefix)
  +
  +    to_set = {}
  +    for key, value in app.conf.items():
  +        key_lower = key.lower()
  +        if key_lower.startswith(prefix) and len(key_lower) > prefix_len:
  +            scout_key = key_lower[prefix_len:]
  +            to_set[scout_key] = value
  +
  +    scout_config.set(**to_set)
   
   
   def uninstall():
  -    before_task_publish.disconnect(before_publish_callback)
  -    task_prerun.disconnect(prerun_callback)
  -    task_postrun.disconnect(postrun_callback)
  +    before_task_publish.disconnect(before_task_publish_callback)
  +    task_prerun.disconnect(task_prerun_callback)
  +    task_postrun.disconnect(task_postrun_callback)
  
verification_info: {"golden_diff": "diff --git a/src/scout_apm/celery.py b/src/scout_apm/celery.py\n--- a/src/scout_apm/celery.py\n+++ b/src/scout_apm/celery.py\n@@ -7,15 +7,16 @@\n \n import scout_apm.core\n from scout_apm.compat import datetime_to_timestamp\n+from scout_apm.core.config import scout_config\n from scout_apm.core.tracked_request import TrackedRequest\n \n \n-def before_publish_callback(headers=None, properties=None, **kwargs):\n+def before_task_publish_callback(headers=None, properties=None, **kwargs):\n     if \"scout_task_start\" not in headers:\n         headers[\"scout_task_start\"] = datetime_to_timestamp(dt.datetime.utcnow())\n \n \n-def prerun_callback(task=None, **kwargs):\n+def task_prerun_callback(task=None, **kwargs):\n     tracked_request = TrackedRequest.instance()\n     tracked_request.is_real_request = True\n \n@@ -45,22 +46,39 @@\n     tracked_request.start_span(operation=(\"Job/\" + task.name))\n \n \n-def postrun_callback(task=None, **kwargs):\n+def task_postrun_callback(task=None, **kwargs):\n     tracked_request = TrackedRequest.instance()\n     tracked_request.stop_span()\n \n \n-def install():\n+def install(app=None):\n+    if app is not None:\n+        copy_configuration(app)\n+\n     installed = scout_apm.core.install()\n     if not installed:\n         return\n \n-    before_task_publish.connect(before_publish_callback)\n-    task_prerun.connect(prerun_callback)\n-    task_postrun.connect(postrun_callback)\n+    before_task_publish.connect(before_task_publish_callback)\n+    task_prerun.connect(task_prerun_callback)\n+    task_postrun.connect(task_postrun_callback)\n+\n+\n+def copy_configuration(app):\n+    prefix = \"scout_\"\n+    prefix_len = len(prefix)\n+\n+    to_set = {}\n+    for key, value in app.conf.items():\n+        key_lower = key.lower()\n+        if key_lower.startswith(prefix) and len(key_lower) > prefix_len:\n+            scout_key = key_lower[prefix_len:]\n+            to_set[scout_key] = value\n+\n+    scout_config.set(**to_set)\n \n \n def uninstall():\n-    before_task_publish.disconnect(before_publish_callback)\n-    task_prerun.disconnect(prerun_callback)\n-    task_postrun.disconnect(postrun_callback)\n+    before_task_publish.disconnect(before_task_publish_callback)\n+    task_prerun.disconnect(task_prerun_callback)\n+    task_postrun.disconnect(task_postrun_callback)\n", "issue": "Require Celery app reference to read configuration\nWe had a customer whose Celery tasks weren't reporting whilst their Django views were. It turns out they had configured in the Django settings file, which isn't applied when Celery runs. This is because it doesn't run \"under\" Django through `manage.py`, but separately through `celery worker`.\r\n\r\nThe django pattern is to use [Celery's `app.config_from_object`](https://docs.celeryproject.org/en/latest/reference/celery.html#celery.Celery.config_from_object) to read the Django settings. If we then read out of there for the scout settings, we would again allow shared configuration between the two.\r\n\r\nThis would need changing the Celery install process to take an `app` argument:\r\n\r\n```python\r\napp = celery.Celery(..)\r\n...\r\nscout_apm.celery.install(app)\r\n```\r\n\r\nWe should work without this for backwards compatibility reasons, but throw a warninng when it's not passed as I predict this issue will appear repeatedly if we don't encourage users this way.\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport datetime as dt\n\nfrom celery.signals import before_task_publish, task_postrun, task_prerun\n\nimport scout_apm.core\nfrom scout_apm.compat import datetime_to_timestamp\nfrom scout_apm.core.tracked_request import TrackedRequest\n\n\ndef before_publish_callback(headers=None, properties=None, **kwargs):\n    if \"scout_task_start\" not in headers:\n        headers[\"scout_task_start\"] = datetime_to_timestamp(dt.datetime.utcnow())\n\n\ndef prerun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.is_real_request = True\n\n    start = getattr(task.request, \"scout_task_start\", None)\n    if start is not None:\n        now = datetime_to_timestamp(dt.datetime.utcnow())\n        try:\n            queue_time = now - start\n        except TypeError:\n            pass\n        else:\n            tracked_request.tag(\"queue_time\", queue_time)\n\n    task_id = getattr(task.request, \"id\", None)\n    if task_id:\n        tracked_request.tag(\"task_id\", task_id)\n    parent_task_id = getattr(task.request, \"parent_id\", None)\n    if parent_task_id:\n        tracked_request.tag(\"parent_task_id\", parent_task_id)\n\n    delivery_info = task.request.delivery_info\n    tracked_request.tag(\"is_eager\", delivery_info.get(\"is_eager\", False))\n    tracked_request.tag(\"exchange\", delivery_info.get(\"exchange\", \"unknown\"))\n    tracked_request.tag(\"routing_key\", delivery_info.get(\"routing_key\", \"unknown\"))\n    tracked_request.tag(\"queue\", delivery_info.get(\"queue\", \"unknown\"))\n\n    tracked_request.start_span(operation=(\"Job/\" + task.name))\n\n\ndef postrun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.stop_span()\n\n\ndef install():\n    installed = scout_apm.core.install()\n    if not installed:\n        return\n\n    before_task_publish.connect(before_publish_callback)\n    task_prerun.connect(prerun_callback)\n    task_postrun.connect(postrun_callback)\n\n\ndef uninstall():\n    before_task_publish.disconnect(before_publish_callback)\n    task_prerun.disconnect(prerun_callback)\n    task_postrun.disconnect(postrun_callback)\n", "path": "src/scout_apm/celery.py"}]}