We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Core agent update to 1.2.9
We've had a few bug fixes and improvements with the CA. Please bump to 1.2.9 in python CA manager.

--- 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/scout_apm/core/config.py`
Content:
```
1 # coding=utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import logging
5 import os
6 import warnings
7 
8 from scout_apm.compat import string_type
9 from scout_apm.core import platform_detection
10 
11 logger = logging.getLogger(__name__)
12 
13 
14 class ScoutConfig(object):
15     """
16     Configuration object for the ScoutApm agent.
17 
18     Contains a list of configuration "layers". When a configuration key is
19     looked up, each layer is asked in turn if it knows the value. The first one
20     to answer affirmatively returns the value.
21     """
22 
23     def __init__(self):
24         self.layers = [
25             Env(),
26             Python(),
27             Derived(self),
28             Defaults(),
29             Null(),
30         ]
31 
32     def value(self, key):
33         value = self.locate_layer_for_key(key).value(key)
34         if key in CONVERSIONS:
35             return CONVERSIONS[key](value)
36         return value
37 
38     def locate_layer_for_key(self, key):
39         for layer in self.layers:
40             if layer.has_config(key):
41                 return layer
42 
43         # Should be unreachable because Null returns None for all keys.
44         raise ValueError("key {!r} not found in any layer".format(key))
45 
46     def log(self):
47         logger.debug("Configuration Loaded:")
48         for key in self.known_keys():
49             layer = self.locate_layer_for_key(key)
50             logger.debug(
51                 "%-9s: %s = %s", layer.__class__.__name__, key, layer.value(key)
52             )
53 
54     def known_keys(self):
55         return [
56             "app_server",
57             "application_root",
58             "core_agent_config_file",
59             "core_agent_dir",
60             "core_agent_download",
61             "core_agent_launch",
62             "core_agent_log_file",
63             "core_agent_log_level",
64             "core_agent_permissions",
65             "core_agent_socket_path",
66             "core_agent_version",
67             "disabled_instruments",
68             "download_url",
69             "framework",
70             "framework_version",
71             "hostname",
72             "ignore",
73             "key",
74             "log_level",
75             "monitor",
76             "name",
77             "revision_sha",
78             "scm_subdirectory",
79             "shutdown_message_enabled",
80             "shutdown_timeout_seconds",
81         ]
82 
83     def core_agent_permissions(self):
84         try:
85             return int(str(self.value("core_agent_permissions")), 8)
86         except ValueError:
87             logger.exception(
88                 "Invalid core_agent_permissions value, using default of 0o700"
89             )
90             return 0o700
91 
92     @classmethod
93     def set(cls, **kwargs):
94         """
95         Sets a configuration value for the Scout agent. Values set here will
96         not override values set in ENV.
97         """
98         for key, value in kwargs.items():
99             SCOUT_PYTHON_VALUES[key] = value
100 
101     @classmethod
102     def unset(cls, *keys):
103         """
104         Removes a configuration value for the Scout agent.
105         """
106         for key in keys:
107             SCOUT_PYTHON_VALUES.pop(key, None)
108 
109     @classmethod
110     def reset_all(cls):
111         """
112         Remove all configuration settings set via `ScoutConfig.set(...)`.
113 
114         This is meant for use in testing.
115         """
116         SCOUT_PYTHON_VALUES.clear()
117 
118 
119 # Module-level data, the ScoutConfig.set(key="value") adds to this
120 SCOUT_PYTHON_VALUES = {}
121 
122 
123 class Python(object):
124     """
125     A configuration overlay that lets other parts of python set values.
126     """
127 
128     def has_config(self, key):
129         return key in SCOUT_PYTHON_VALUES
130 
131     def value(self, key):
132         return SCOUT_PYTHON_VALUES[key]
133 
134 
135 class Env(object):
136     """
137     Reads configuration from environment by prefixing the key
138     requested with "SCOUT_"
139 
140     Example: the `key` config looks for SCOUT_KEY
141     environment variable
142     """
143 
144     def has_config(self, key):
145         env_key = self.modify_key(key)
146         return env_key in os.environ
147 
148     def value(self, key):
149         env_key = self.modify_key(key)
150         return os.environ[env_key]
151 
152     def modify_key(self, key):
153         env_key = ("SCOUT_" + key).upper()
154         return env_key
155 
156 
157 class Derived(object):
158     """
159     A configuration overlay that calculates from other values.
160     """
161 
162     def __init__(self, config):
163         """
164         config argument is the overall ScoutConfig var, so we can lookup the
165         components of the derived info.
166         """
167         self.config = config
168 
169     def has_config(self, key):
170         return self.lookup_func(key) is not None
171 
172     def value(self, key):
173         return self.lookup_func(key)()
174 
175     def lookup_func(self, key):
176         """
177         Returns the derive_#{key} function, or None if it isn't defined
178         """
179         func_name = "derive_" + key
180         return getattr(self, func_name, None)
181 
182     def derive_core_agent_socket_path(self):
183         return "{}/{}/scout-agent.sock".format(
184             self.config.value("core_agent_dir"),
185             self.config.value("core_agent_full_name"),
186         )
187 
188     def derive_core_agent_full_name(self):
189         triple = self.config.value("core_agent_triple")
190         if not platform_detection.is_valid_triple(triple):
191             warnings.warn("Invalid value for core_agent_triple: {}".format(triple))
192         return "{name}-{version}-{triple}".format(
193             name="scout_apm_core",
194             version=self.config.value("core_agent_version"),
195             triple=triple,
196         )
197 
198     def derive_core_agent_triple(self):
199         return platform_detection.get_triple()
200 
201 
202 class Defaults(object):
203     """
204     Provides default values for important configurations
205     """
206 
207     def __init__(self):
208         self.defaults = {
209             "app_server": "",
210             "application_root": "",
211             "core_agent_dir": "/tmp/scout_apm_core",
212             "core_agent_download": True,
213             "core_agent_launch": True,
214             "core_agent_log_level": "info",
215             "core_agent_permissions": 700,
216             "core_agent_version": "v1.2.6",  # can be an exact tag name, or 'latest'
217             "disabled_instruments": [],
218             "download_url": "https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release",  # noqa: E501
219             "framework": "",
220             "framework_version": "",
221             "hostname": None,
222             "key": "",
223             "monitor": False,
224             "name": "Python App",
225             "revision_sha": self._git_revision_sha(),
226             "scm_subdirectory": "",
227             "shutdown_message_enabled": True,
228             "shutdown_timeout_seconds": 2.0,
229             "uri_reporting": "filtered_params",
230         }
231 
232     def _git_revision_sha(self):
233         # N.B. The environment variable SCOUT_REVISION_SHA may also be used,
234         # but that will be picked up by Env
235         return os.environ.get("HEROKU_SLUG_COMMIT", "")
236 
237     def has_config(self, key):
238         return key in self.defaults
239 
240     def value(self, key):
241         return self.defaults[key]
242 
243 
244 class Null(object):
245     """
246     Always answers that a key is present, but the value is None
247 
248     Used as the last step of the layered configuration.
249     """
250 
251     def has_config(self, key):
252         return True
253 
254     def value(self, key):
255         return None
256 
257 
258 def convert_to_bool(value):
259     if isinstance(value, bool):
260         return value
261     if isinstance(value, string_type):
262         return value.lower() in ("yes", "true", "t", "1")
263     # Unknown type - default to false?
264     return False
265 
266 
267 def convert_to_float(value):
268     try:
269         return float(value)
270     except ValueError:
271         return 0.0
272 
273 
274 def convert_to_list(value):
275     if isinstance(value, list):
276         return value
277     if isinstance(value, tuple):
278         return list(value)
279     if isinstance(value, string_type):
280         # Split on commas
281         return [item.strip() for item in value.split(",") if item]
282     # Unknown type - default to empty?
283     return []
284 
285 
286 CONVERSIONS = {
287     "core_agent_download": convert_to_bool,
288     "core_agent_launch": convert_to_bool,
289     "disabled_instruments": convert_to_list,
290     "ignore": convert_to_list,
291     "monitor": convert_to_bool,
292     "shutdown_message_enabled": convert_to_bool,
293     "shutdown_timeout_seconds": convert_to_float,
294 }
295 
296 
297 scout_config = ScoutConfig()
298
```
--- 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/scout_apm/core/config.py b/src/scout_apm/core/config.py
--- a/src/scout_apm/core/config.py
+++ b/src/scout_apm/core/config.py
@@ -213,7 +213,7 @@
             "core_agent_launch": True,
             "core_agent_log_level": "info",
             "core_agent_permissions": 700,
-            "core_agent_version": "v1.2.6",  # can be an exact tag name, or 'latest'
+            "core_agent_version": "v1.2.9",  # can be an exact tag name, or 'latest'
             "disabled_instruments": [],
             "download_url": "https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release",  # noqa: E501
             "framework": "",

{"golden_diff": "diff --git a/src/scout_apm/core/config.py b/src/scout_apm/core/config.py\n--- a/src/scout_apm/core/config.py\n+++ b/src/scout_apm/core/config.py\n@@ -213,7 +213,7 @@\n             \"core_agent_launch\": True,\n             \"core_agent_log_level\": \"info\",\n             \"core_agent_permissions\": 700,\n-            \"core_agent_version\": \"v1.2.6\",  # can be an exact tag name, or 'latest'\n+            \"core_agent_version\": \"v1.2.9\",  # can be an exact tag name, or 'latest'\n             \"disabled_instruments\": [],\n             \"download_url\": \"https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release\",  # noqa: E501\n             \"framework\": \"\",\n", "issue": "Core agent update to 1.2.9\nWe've had a few bug fixes and improvements with the CA. Please bump to 1.2.9 in python CA manager.\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\nimport os\nimport warnings\n\nfrom scout_apm.compat import string_type\nfrom scout_apm.core import platform_detection\n\nlogger = logging.getLogger(__name__)\n\n\nclass ScoutConfig(object):\n    \"\"\"\n    Configuration object for the ScoutApm agent.\n\n    Contains a list of configuration \"layers\". When a configuration key is\n    looked up, each layer is asked in turn if it knows the value. The first one\n    to answer affirmatively returns the value.\n    \"\"\"\n\n    def __init__(self):\n        self.layers = [\n            Env(),\n            Python(),\n            Derived(self),\n            Defaults(),\n            Null(),\n        ]\n\n    def value(self, key):\n        value = self.locate_layer_for_key(key).value(key)\n        if key in CONVERSIONS:\n            return CONVERSIONS[key](value)\n        return value\n\n    def locate_layer_for_key(self, key):\n        for layer in self.layers:\n            if layer.has_config(key):\n                return layer\n\n        # Should be unreachable because Null returns None for all keys.\n        raise ValueError(\"key {!r} not found in any layer\".format(key))\n\n    def log(self):\n        logger.debug(\"Configuration Loaded:\")\n        for key in self.known_keys():\n            layer = self.locate_layer_for_key(key)\n            logger.debug(\n                \"%-9s: %s = %s\", layer.__class__.__name__, key, layer.value(key)\n            )\n\n    def known_keys(self):\n        return [\n            \"app_server\",\n            \"application_root\",\n            \"core_agent_config_file\",\n            \"core_agent_dir\",\n            \"core_agent_download\",\n            \"core_agent_launch\",\n            \"core_agent_log_file\",\n            \"core_agent_log_level\",\n            \"core_agent_permissions\",\n            \"core_agent_socket_path\",\n            \"core_agent_version\",\n            \"disabled_instruments\",\n            \"download_url\",\n            \"framework\",\n            \"framework_version\",\n            \"hostname\",\n            \"ignore\",\n            \"key\",\n            \"log_level\",\n            \"monitor\",\n            \"name\",\n            \"revision_sha\",\n            \"scm_subdirectory\",\n            \"shutdown_message_enabled\",\n            \"shutdown_timeout_seconds\",\n        ]\n\n    def core_agent_permissions(self):\n        try:\n            return int(str(self.value(\"core_agent_permissions\")), 8)\n        except ValueError:\n            logger.exception(\n                \"Invalid core_agent_permissions value, using default of 0o700\"\n            )\n            return 0o700\n\n    @classmethod\n    def set(cls, **kwargs):\n        \"\"\"\n        Sets a configuration value for the Scout agent. Values set here will\n        not override values set in ENV.\n        \"\"\"\n        for key, value in kwargs.items():\n            SCOUT_PYTHON_VALUES[key] = value\n\n    @classmethod\n    def unset(cls, *keys):\n        \"\"\"\n        Removes a configuration value for the Scout agent.\n        \"\"\"\n        for key in keys:\n            SCOUT_PYTHON_VALUES.pop(key, None)\n\n    @classmethod\n    def reset_all(cls):\n        \"\"\"\n        Remove all configuration settings set via `ScoutConfig.set(...)`.\n\n        This is meant for use in testing.\n        \"\"\"\n        SCOUT_PYTHON_VALUES.clear()\n\n\n# Module-level data, the ScoutConfig.set(key=\"value\") adds to this\nSCOUT_PYTHON_VALUES = {}\n\n\nclass Python(object):\n    \"\"\"\n    A configuration overlay that lets other parts of python set values.\n    \"\"\"\n\n    def has_config(self, key):\n        return key in SCOUT_PYTHON_VALUES\n\n    def value(self, key):\n        return SCOUT_PYTHON_VALUES[key]\n\n\nclass Env(object):\n    \"\"\"\n    Reads configuration from environment by prefixing the key\n    requested with \"SCOUT_\"\n\n    Example: the `key` config looks for SCOUT_KEY\n    environment variable\n    \"\"\"\n\n    def has_config(self, key):\n        env_key = self.modify_key(key)\n        return env_key in os.environ\n\n    def value(self, key):\n        env_key = self.modify_key(key)\n        return os.environ[env_key]\n\n    def modify_key(self, key):\n        env_key = (\"SCOUT_\" + key).upper()\n        return env_key\n\n\nclass Derived(object):\n    \"\"\"\n    A configuration overlay that calculates from other values.\n    \"\"\"\n\n    def __init__(self, config):\n        \"\"\"\n        config argument is the overall ScoutConfig var, so we can lookup the\n        components of the derived info.\n        \"\"\"\n        self.config = config\n\n    def has_config(self, key):\n        return self.lookup_func(key) is not None\n\n    def value(self, key):\n        return self.lookup_func(key)()\n\n    def lookup_func(self, key):\n        \"\"\"\n        Returns the derive_#{key} function, or None if it isn't defined\n        \"\"\"\n        func_name = \"derive_\" + key\n        return getattr(self, func_name, None)\n\n    def derive_core_agent_socket_path(self):\n        return \"{}/{}/scout-agent.sock\".format(\n            self.config.value(\"core_agent_dir\"),\n            self.config.value(\"core_agent_full_name\"),\n        )\n\n    def derive_core_agent_full_name(self):\n        triple = self.config.value(\"core_agent_triple\")\n        if not platform_detection.is_valid_triple(triple):\n            warnings.warn(\"Invalid value for core_agent_triple: {}\".format(triple))\n        return \"{name}-{version}-{triple}\".format(\n            name=\"scout_apm_core\",\n            version=self.config.value(\"core_agent_version\"),\n            triple=triple,\n        )\n\n    def derive_core_agent_triple(self):\n        return platform_detection.get_triple()\n\n\nclass Defaults(object):\n    \"\"\"\n    Provides default values for important configurations\n    \"\"\"\n\n    def __init__(self):\n        self.defaults = {\n            \"app_server\": \"\",\n            \"application_root\": \"\",\n            \"core_agent_dir\": \"/tmp/scout_apm_core\",\n            \"core_agent_download\": True,\n            \"core_agent_launch\": True,\n            \"core_agent_log_level\": \"info\",\n            \"core_agent_permissions\": 700,\n            \"core_agent_version\": \"v1.2.6\",  # can be an exact tag name, or 'latest'\n            \"disabled_instruments\": [],\n            \"download_url\": \"https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release\",  # noqa: E501\n            \"framework\": \"\",\n            \"framework_version\": \"\",\n            \"hostname\": None,\n            \"key\": \"\",\n            \"monitor\": False,\n            \"name\": \"Python App\",\n            \"revision_sha\": self._git_revision_sha(),\n            \"scm_subdirectory\": \"\",\n            \"shutdown_message_enabled\": True,\n            \"shutdown_timeout_seconds\": 2.0,\n            \"uri_reporting\": \"filtered_params\",\n        }\n\n    def _git_revision_sha(self):\n        # N.B. The environment variable SCOUT_REVISION_SHA may also be used,\n        # but that will be picked up by Env\n        return os.environ.get(\"HEROKU_SLUG_COMMIT\", \"\")\n\n    def has_config(self, key):\n        return key in self.defaults\n\n    def value(self, key):\n        return self.defaults[key]\n\n\nclass Null(object):\n    \"\"\"\n    Always answers that a key is present, but the value is None\n\n    Used as the last step of the layered configuration.\n    \"\"\"\n\n    def has_config(self, key):\n        return True\n\n    def value(self, key):\n        return None\n\n\ndef convert_to_bool(value):\n    if isinstance(value, bool):\n        return value\n    if isinstance(value, string_type):\n        return value.lower() in (\"yes\", \"true\", \"t\", \"1\")\n    # Unknown type - default to false?\n    return False\n\n\ndef convert_to_float(value):\n    try:\n        return float(value)\n    except ValueError:\n        return 0.0\n\n\ndef convert_to_list(value):\n    if isinstance(value, list):\n        return value\n    if isinstance(value, tuple):\n        return list(value)\n    if isinstance(value, string_type):\n        # Split on commas\n        return [item.strip() for item in value.split(\",\") if item]\n    # Unknown type - default to empty?\n    return []\n\n\nCONVERSIONS = {\n    \"core_agent_download\": convert_to_bool,\n    \"core_agent_launch\": convert_to_bool,\n    \"disabled_instruments\": convert_to_list,\n    \"ignore\": convert_to_list,\n    \"monitor\": convert_to_bool,\n    \"shutdown_message_enabled\": convert_to_bool,\n    \"shutdown_timeout_seconds\": convert_to_float,\n}\n\n\nscout_config = ScoutConfig()\n", "path": "src/scout_apm/core/config.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\nimport os\nimport warnings\n\nfrom scout_apm.compat import string_type\nfrom scout_apm.core import platform_detection\n\nlogger = logging.getLogger(__name__)\n\n\nclass ScoutConfig(object):\n    \"\"\"\n    Configuration object for the ScoutApm agent.\n\n    Contains a list of configuration \"layers\". When a configuration key is\n    looked up, each layer is asked in turn if it knows the value. The first one\n    to answer affirmatively returns the value.\n    \"\"\"\n\n    def __init__(self):\n        self.layers = [\n            Env(),\n            Python(),\n            Derived(self),\n            Defaults(),\n            Null(),\n        ]\n\n    def value(self, key):\n        value = self.locate_layer_for_key(key).value(key)\n        if key in CONVERSIONS:\n            return CONVERSIONS[key](value)\n        return value\n\n    def locate_layer_for_key(self, key):\n        for layer in self.layers:\n            if layer.has_config(key):\n                return layer\n\n        # Should be unreachable because Null returns None for all keys.\n        raise ValueError(\"key {!r} not found in any layer\".format(key))\n\n    def log(self):\n        logger.debug(\"Configuration Loaded:\")\n        for key in self.known_keys():\n            layer = self.locate_layer_for_key(key)\n            logger.debug(\n                \"%-9s: %s = %s\", layer.__class__.__name__, key, layer.value(key)\n            )\n\n    def known_keys(self):\n        return [\n            \"app_server\",\n            \"application_root\",\n            \"core_agent_config_file\",\n            \"core_agent_dir\",\n            \"core_agent_download\",\n            \"core_agent_launch\",\n            \"core_agent_log_file\",\n            \"core_agent_log_level\",\n            \"core_agent_permissions\",\n            \"core_agent_socket_path\",\n            \"core_agent_version\",\n            \"disabled_instruments\",\n            \"download_url\",\n            \"framework\",\n            \"framework_version\",\n            \"hostname\",\n            \"ignore\",\n            \"key\",\n            \"log_level\",\n            \"monitor\",\n            \"name\",\n            \"revision_sha\",\n            \"scm_subdirectory\",\n            \"shutdown_message_enabled\",\n            \"shutdown_timeout_seconds\",\n        ]\n\n    def core_agent_permissions(self):\n        try:\n            return int(str(self.value(\"core_agent_permissions\")), 8)\n        except ValueError:\n            logger.exception(\n                \"Invalid core_agent_permissions value, using default of 0o700\"\n            )\n            return 0o700\n\n    @classmethod\n    def set(cls, **kwargs):\n        \"\"\"\n        Sets a configuration value for the Scout agent. Values set here will\n        not override values set in ENV.\n        \"\"\"\n        for key, value in kwargs.items():\n            SCOUT_PYTHON_VALUES[key] = value\n\n    @classmethod\n    def unset(cls, *keys):\n        \"\"\"\n        Removes a configuration value for the Scout agent.\n        \"\"\"\n        for key in keys:\n            SCOUT_PYTHON_VALUES.pop(key, None)\n\n    @classmethod\n    def reset_all(cls):\n        \"\"\"\n        Remove all configuration settings set via `ScoutConfig.set(...)`.\n\n        This is meant for use in testing.\n        \"\"\"\n        SCOUT_PYTHON_VALUES.clear()\n\n\n# Module-level data, the ScoutConfig.set(key=\"value\") adds to this\nSCOUT_PYTHON_VALUES = {}\n\n\nclass Python(object):\n    \"\"\"\n    A configuration overlay that lets other parts of python set values.\n    \"\"\"\n\n    def has_config(self, key):\n        return key in SCOUT_PYTHON_VALUES\n\n    def value(self, key):\n        return SCOUT_PYTHON_VALUES[key]\n\n\nclass Env(object):\n    \"\"\"\n    Reads configuration from environment by prefixing the key\n    requested with \"SCOUT_\"\n\n    Example: the `key` config looks for SCOUT_KEY\n    environment variable\n    \"\"\"\n\n    def has_config(self, key):\n        env_key = self.modify_key(key)\n        return env_key in os.environ\n\n    def value(self, key):\n        env_key = self.modify_key(key)\n        return os.environ[env_key]\n\n    def modify_key(self, key):\n        env_key = (\"SCOUT_\" + key).upper()\n        return env_key\n\n\nclass Derived(object):\n    \"\"\"\n    A configuration overlay that calculates from other values.\n    \"\"\"\n\n    def __init__(self, config):\n        \"\"\"\n        config argument is the overall ScoutConfig var, so we can lookup the\n        components of the derived info.\n        \"\"\"\n        self.config = config\n\n    def has_config(self, key):\n        return self.lookup_func(key) is not None\n\n    def value(self, key):\n        return self.lookup_func(key)()\n\n    def lookup_func(self, key):\n        \"\"\"\n        Returns the derive_#{key} function, or None if it isn't defined\n        \"\"\"\n        func_name = \"derive_\" + key\n        return getattr(self, func_name, None)\n\n    def derive_core_agent_socket_path(self):\n        return \"{}/{}/scout-agent.sock\".format(\n            self.config.value(\"core_agent_dir\"),\n            self.config.value(\"core_agent_full_name\"),\n        )\n\n    def derive_core_agent_full_name(self):\n        triple = self.config.value(\"core_agent_triple\")\n        if not platform_detection.is_valid_triple(triple):\n            warnings.warn(\"Invalid value for core_agent_triple: {}\".format(triple))\n        return \"{name}-{version}-{triple}\".format(\n            name=\"scout_apm_core\",\n            version=self.config.value(\"core_agent_version\"),\n            triple=triple,\n        )\n\n    def derive_core_agent_triple(self):\n        return platform_detection.get_triple()\n\n\nclass Defaults(object):\n    \"\"\"\n    Provides default values for important configurations\n    \"\"\"\n\n    def __init__(self):\n        self.defaults = {\n            \"app_server\": \"\",\n            \"application_root\": \"\",\n            \"core_agent_dir\": \"/tmp/scout_apm_core\",\n            \"core_agent_download\": True,\n            \"core_agent_launch\": True,\n            \"core_agent_log_level\": \"info\",\n            \"core_agent_permissions\": 700,\n            \"core_agent_version\": \"v1.2.9\",  # can be an exact tag name, or 'latest'\n            \"disabled_instruments\": [],\n            \"download_url\": \"https://s3-us-west-1.amazonaws.com/scout-public-downloads/apm_core_agent/release\",  # noqa: E501\n            \"framework\": \"\",\n            \"framework_version\": \"\",\n            \"hostname\": None,\n            \"key\": \"\",\n            \"monitor\": False,\n            \"name\": \"Python App\",\n            \"revision_sha\": self._git_revision_sha(),\n            \"scm_subdirectory\": \"\",\n            \"shutdown_message_enabled\": True,\n            \"shutdown_timeout_seconds\": 2.0,\n            \"uri_reporting\": \"filtered_params\",\n        }\n\n    def _git_revision_sha(self):\n        # N.B. The environment variable SCOUT_REVISION_SHA may also be used,\n        # but that will be picked up by Env\n        return os.environ.get(\"HEROKU_SLUG_COMMIT\", \"\")\n\n    def has_config(self, key):\n        return key in self.defaults\n\n    def value(self, key):\n        return self.defaults[key]\n\n\nclass Null(object):\n    \"\"\"\n    Always answers that a key is present, but the value is None\n\n    Used as the last step of the layered configuration.\n    \"\"\"\n\n    def has_config(self, key):\n        return True\n\n    def value(self, key):\n        return None\n\n\ndef convert_to_bool(value):\n    if isinstance(value, bool):\n        return value\n    if isinstance(value, string_type):\n        return value.lower() in (\"yes\", \"true\", \"t\", \"1\")\n    # Unknown type - default to false?\n    return False\n\n\ndef convert_to_float(value):\n    try:\n        return float(value)\n    except ValueError:\n        return 0.0\n\n\ndef convert_to_list(value):\n    if isinstance(value, list):\n        return value\n    if isinstance(value, tuple):\n        return list(value)\n    if isinstance(value, string_type):\n        # Split on commas\n        return [item.strip() for item in value.split(\",\") if item]\n    # Unknown type - default to empty?\n    return []\n\n\nCONVERSIONS = {\n    \"core_agent_download\": convert_to_bool,\n    \"core_agent_launch\": convert_to_bool,\n    \"disabled_instruments\": convert_to_list,\n    \"ignore\": convert_to_list,\n    \"monitor\": convert_to_bool,\n    \"shutdown_message_enabled\": convert_to_bool,\n    \"shutdown_timeout_seconds\": convert_to_float,\n}\n\n\nscout_config = ScoutConfig()\n", "path": "src/scout_apm/core/config.py"}]}