scout-apm 3.3.0__cp38-cp38-musllinux_1_2_i686.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. scout_apm/__init__.py +0 -0
  2. scout_apm/api/__init__.py +197 -0
  3. scout_apm/async_/__init__.py +1 -0
  4. scout_apm/async_/api.py +41 -0
  5. scout_apm/async_/instruments/__init__.py +0 -0
  6. scout_apm/async_/instruments/jinja2.py +13 -0
  7. scout_apm/async_/starlette.py +101 -0
  8. scout_apm/bottle.py +86 -0
  9. scout_apm/celery.py +153 -0
  10. scout_apm/compat.py +104 -0
  11. scout_apm/core/__init__.py +99 -0
  12. scout_apm/core/_objtrace.cpython-38-i386-linux-gnu.so +0 -0
  13. scout_apm/core/agent/__init__.py +0 -0
  14. scout_apm/core/agent/commands.py +250 -0
  15. scout_apm/core/agent/manager.py +319 -0
  16. scout_apm/core/agent/socket.py +211 -0
  17. scout_apm/core/backtrace.py +116 -0
  18. scout_apm/core/cli/__init__.py +0 -0
  19. scout_apm/core/cli/core_agent_manager.py +32 -0
  20. scout_apm/core/config.py +404 -0
  21. scout_apm/core/context.py +140 -0
  22. scout_apm/core/error.py +95 -0
  23. scout_apm/core/error_service.py +167 -0
  24. scout_apm/core/metadata.py +66 -0
  25. scout_apm/core/n_plus_one_tracker.py +41 -0
  26. scout_apm/core/objtrace.py +24 -0
  27. scout_apm/core/platform_detection.py +66 -0
  28. scout_apm/core/queue_time.py +99 -0
  29. scout_apm/core/sampler.py +149 -0
  30. scout_apm/core/samplers/__init__.py +0 -0
  31. scout_apm/core/samplers/cpu.py +76 -0
  32. scout_apm/core/samplers/memory.py +23 -0
  33. scout_apm/core/samplers/thread.py +41 -0
  34. scout_apm/core/stacktracer.py +30 -0
  35. scout_apm/core/threading.py +56 -0
  36. scout_apm/core/tracked_request.py +328 -0
  37. scout_apm/core/web_requests.py +167 -0
  38. scout_apm/django/__init__.py +7 -0
  39. scout_apm/django/apps.py +137 -0
  40. scout_apm/django/instruments/__init__.py +0 -0
  41. scout_apm/django/instruments/huey.py +30 -0
  42. scout_apm/django/instruments/sql.py +140 -0
  43. scout_apm/django/instruments/template.py +35 -0
  44. scout_apm/django/middleware.py +211 -0
  45. scout_apm/django/request.py +144 -0
  46. scout_apm/dramatiq.py +42 -0
  47. scout_apm/falcon.py +142 -0
  48. scout_apm/flask/__init__.py +118 -0
  49. scout_apm/flask/sqlalchemy.py +28 -0
  50. scout_apm/huey.py +54 -0
  51. scout_apm/hug.py +40 -0
  52. scout_apm/instruments/__init__.py +21 -0
  53. scout_apm/instruments/elasticsearch.py +263 -0
  54. scout_apm/instruments/jinja2.py +127 -0
  55. scout_apm/instruments/pymongo.py +105 -0
  56. scout_apm/instruments/redis.py +77 -0
  57. scout_apm/instruments/urllib3.py +80 -0
  58. scout_apm/rq.py +85 -0
  59. scout_apm/sqlalchemy.py +38 -0
  60. scout_apm-3.3.0.dist-info/LICENSE +21 -0
  61. scout_apm-3.3.0.dist-info/METADATA +82 -0
  62. scout_apm-3.3.0.dist-info/RECORD +65 -0
  63. scout_apm-3.3.0.dist-info/WHEEL +5 -0
  64. scout_apm-3.3.0.dist-info/entry_points.txt +2 -0
  65. scout_apm-3.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,99 @@
1
+ # coding=utf-8
2
+
3
+ import atexit
4
+ import logging
5
+ import os
6
+ import sys
7
+
8
+ from scout_apm import instruments
9
+ from scout_apm.compat import kwargs_only
10
+ from scout_apm.core import objtrace
11
+ from scout_apm.core.agent.manager import CoreAgentManager
12
+ from scout_apm.core.agent.socket import CoreAgentSocketThread
13
+ from scout_apm.core.config import scout_config
14
+ from scout_apm.core.error_service import ErrorServiceThread
15
+ from scout_apm.core.metadata import report_app_metadata
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @kwargs_only
21
+ def install(config=None):
22
+ global shutdown_registered
23
+ if config is not None:
24
+ scout_config.set(**config)
25
+ scout_config.log()
26
+
27
+ if os.name == "nt":
28
+ logger.info(
29
+ "APM Not Launching on PID: %s - Windows is not supported", os.getpid()
30
+ )
31
+ return False
32
+
33
+ if not scout_config.value("monitor"):
34
+ logger.info(
35
+ "APM Not Launching on PID: %s - Configuration 'monitor' is not true",
36
+ os.getpid(),
37
+ )
38
+ return False
39
+
40
+ instruments.ensure_all_installed()
41
+ objtrace.enable()
42
+
43
+ logger.debug("APM Launching on PID: %s", os.getpid())
44
+ launched = CoreAgentManager().launch()
45
+
46
+ report_app_metadata()
47
+ if launched:
48
+ # Stop the thread to avoid running threads pre-fork
49
+ CoreAgentSocketThread.ensure_stopped()
50
+
51
+ if scout_config.value("shutdown_timeout_seconds") > 0.0 and not shutdown_registered:
52
+ atexit.register(shutdown)
53
+ shutdown_registered = True
54
+
55
+ return True
56
+
57
+
58
+ shutdown_registered = False
59
+
60
+
61
+ def shutdown():
62
+ timeout_seconds = scout_config.value("shutdown_timeout_seconds")
63
+
64
+ def apm_callback(queue_size):
65
+ if scout_config.value("shutdown_message_enabled"):
66
+ print( # noqa: T001,T201
67
+ (
68
+ "Scout draining {queue_size} event{s} for up to"
69
+ + " {timeout_seconds} seconds"
70
+ ).format(
71
+ queue_size=queue_size,
72
+ s=("" if queue_size == 1 else "s"),
73
+ timeout_seconds=timeout_seconds,
74
+ ),
75
+ file=sys.stderr,
76
+ )
77
+
78
+ def error_callback(queue_size):
79
+ if scout_config.value("shutdown_message_enabled"):
80
+ print( # noqa: T001,T201
81
+ (
82
+ "Scout draining {queue_size} error{s} for up to"
83
+ + " {timeout_seconds} seconds"
84
+ ).format(
85
+ queue_size=queue_size,
86
+ s=("" if queue_size == 1 else "s"),
87
+ timeout_seconds=timeout_seconds,
88
+ ),
89
+ file=sys.stderr,
90
+ )
91
+
92
+ CoreAgentSocketThread.wait_until_drained(
93
+ timeout_seconds=timeout_seconds, callback=apm_callback
94
+ )
95
+
96
+ if scout_config.value("errors_enabled"):
97
+ ErrorServiceThread.wait_until_drained(
98
+ timeout_seconds=timeout_seconds, callback=error_callback
99
+ )
File without changes
@@ -0,0 +1,250 @@
1
+ # coding=utf-8
2
+
3
+ import datetime as dt
4
+ import logging
5
+ import re
6
+
7
+ from scout_apm.compat import iteritems
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ key_regex = re.compile(r"^[a-zA-Z0-9]{20}$")
12
+
13
+
14
+ def format_dt_for_core_agent(event_time: dt.datetime) -> str:
15
+ """
16
+ Returns expected format for Core Agent compatibility.
17
+ Coerce any tz-aware datetime to UTC just in case.
18
+ """
19
+ # if we somehow got a naive datetime, convert it to UTC
20
+ if event_time.tzinfo is None:
21
+ logger.warning("Naive datetime passed to format_dt_for_core_agent")
22
+ event_time = event_time.astimezone(dt.timezone.utc)
23
+ return event_time.isoformat()
24
+
25
+
26
+ class Register(object):
27
+ __slots__ = ("app", "key", "hostname")
28
+
29
+ def __init__(self, app, key, hostname):
30
+ self.app = app
31
+ self.key = key
32
+ self.hostname = hostname
33
+
34
+ def message(self):
35
+ key_prefix = self.key[:3]
36
+ key_matches_regex = bool(key_regex.match(self.key))
37
+ logger.info(
38
+ "Registering with app=%s key_prefix=%s key_format_validated=%s host=%s"
39
+ % (self.app, key_prefix, key_matches_regex, self.hostname)
40
+ )
41
+ return {
42
+ "Register": {
43
+ "app": self.app,
44
+ "key": self.key,
45
+ "host": self.hostname,
46
+ "language": "python",
47
+ "api_version": "1.0",
48
+ }
49
+ }
50
+
51
+
52
+ class StartSpan(object):
53
+ __slots__ = ("timestamp", "request_id", "span_id", "parent", "operation")
54
+
55
+ def __init__(self, timestamp, request_id, span_id, parent, operation):
56
+ self.timestamp = timestamp
57
+ self.request_id = request_id
58
+ self.span_id = span_id
59
+ self.parent = parent
60
+ self.operation = operation
61
+
62
+ def message(self):
63
+ return {
64
+ "StartSpan": {
65
+ "timestamp": format_dt_for_core_agent(self.timestamp),
66
+ "request_id": self.request_id,
67
+ "span_id": self.span_id,
68
+ "parent_id": self.parent,
69
+ "operation": self.operation,
70
+ }
71
+ }
72
+
73
+
74
+ class StopSpan(object):
75
+ __slots__ = ("timestamp", "request_id", "span_id")
76
+
77
+ def __init__(self, timestamp, request_id, span_id):
78
+ self.timestamp = timestamp
79
+ self.request_id = request_id
80
+ self.span_id = span_id
81
+
82
+ def message(self):
83
+ return {
84
+ "StopSpan": {
85
+ "timestamp": format_dt_for_core_agent(self.timestamp),
86
+ "request_id": self.request_id,
87
+ "span_id": self.span_id,
88
+ }
89
+ }
90
+
91
+
92
+ class StartRequest(object):
93
+ __slots__ = ("timestamp", "request_id")
94
+
95
+ def __init__(self, timestamp, request_id):
96
+ self.timestamp = timestamp
97
+ self.request_id = request_id
98
+
99
+ def message(self):
100
+ return {
101
+ "StartRequest": {
102
+ "timestamp": format_dt_for_core_agent(self.timestamp),
103
+ "request_id": self.request_id,
104
+ }
105
+ }
106
+
107
+
108
+ class FinishRequest(object):
109
+ __slots__ = ("timestamp", "request_id")
110
+
111
+ def __init__(self, timestamp, request_id):
112
+ self.timestamp = timestamp
113
+ self.request_id = request_id
114
+
115
+ def message(self):
116
+ return {
117
+ "FinishRequest": {
118
+ "timestamp": format_dt_for_core_agent(self.timestamp),
119
+ "request_id": self.request_id,
120
+ }
121
+ }
122
+
123
+
124
+ class TagSpan(object):
125
+ __slots__ = ("timestamp", "request_id", "span_id", "tag", "value")
126
+
127
+ def __init__(self, timestamp, request_id, span_id, tag, value):
128
+ self.timestamp = timestamp
129
+ self.request_id = request_id
130
+ self.span_id = span_id
131
+ self.tag = tag
132
+ self.value = value
133
+
134
+ def message(self):
135
+ return {
136
+ "TagSpan": {
137
+ "timestamp": format_dt_for_core_agent(self.timestamp),
138
+ "request_id": self.request_id,
139
+ "span_id": self.span_id,
140
+ "tag": self.tag,
141
+ "value": self.value,
142
+ }
143
+ }
144
+
145
+
146
+ class TagRequest(object):
147
+ __slots__ = ("timestamp", "request_id", "tag", "value")
148
+
149
+ def __init__(self, timestamp, request_id, tag, value):
150
+ self.timestamp = timestamp
151
+ self.request_id = request_id
152
+ self.tag = tag
153
+ self.value = value
154
+
155
+ def message(self):
156
+ return {
157
+ "TagRequest": {
158
+ "timestamp": format_dt_for_core_agent(self.timestamp),
159
+ "request_id": self.request_id,
160
+ "tag": self.tag,
161
+ "value": self.value,
162
+ }
163
+ }
164
+
165
+
166
+ class ApplicationEvent(object):
167
+ __slots__ = ("event_type", "event_value", "source", "timestamp")
168
+
169
+ def __init__(self, event_type, event_value, source, timestamp):
170
+ self.event_type = event_type
171
+ self.event_value = event_value
172
+ self.source = source
173
+ self.timestamp = timestamp
174
+
175
+ def message(self):
176
+ return {
177
+ "ApplicationEvent": {
178
+ "timestamp": format_dt_for_core_agent(self.timestamp),
179
+ "event_type": self.event_type,
180
+ "event_value": self.event_value,
181
+ "source": self.source,
182
+ }
183
+ }
184
+
185
+
186
+ class BatchCommand(object):
187
+ __slots__ = ("commands",)
188
+
189
+ def __init__(self, commands):
190
+ self.commands = commands
191
+
192
+ def message(self):
193
+ return {
194
+ "BatchCommand": {
195
+ "commands": [command.message() for command in self.commands]
196
+ }
197
+ }
198
+
199
+ @classmethod
200
+ def from_tracked_request(cls, request):
201
+ # The TrackedRequest must be finished
202
+ commands = []
203
+ commands.append(
204
+ StartRequest(timestamp=request.start_time, request_id=request.request_id)
205
+ )
206
+ for key, value in iteritems(request.tags):
207
+ commands.append(
208
+ TagRequest(
209
+ timestamp=request.start_time,
210
+ request_id=request.request_id,
211
+ tag=key,
212
+ value=value,
213
+ )
214
+ )
215
+
216
+ for span in request.complete_spans:
217
+ commands.append(
218
+ StartSpan(
219
+ timestamp=span.start_time,
220
+ request_id=span.request_id,
221
+ span_id=span.span_id,
222
+ parent=span.parent,
223
+ operation=span.operation,
224
+ )
225
+ )
226
+
227
+ for key, value in iteritems(span.tags):
228
+ commands.append(
229
+ TagSpan(
230
+ timestamp=span.start_time,
231
+ request_id=request.request_id,
232
+ span_id=span.span_id,
233
+ tag=key,
234
+ value=value,
235
+ )
236
+ )
237
+
238
+ commands.append(
239
+ StopSpan(
240
+ timestamp=span.end_time,
241
+ request_id=span.request_id,
242
+ span_id=span.span_id,
243
+ )
244
+ )
245
+
246
+ commands.append(
247
+ FinishRequest(timestamp=request.end_time, request_id=request.request_id)
248
+ )
249
+
250
+ return cls(commands)
@@ -0,0 +1,319 @@
1
+ # coding=utf-8
2
+
3
+ import errno
4
+ import hashlib
5
+ import json
6
+ import logging
7
+ import os
8
+ import subprocess
9
+ import tarfile
10
+ import time
11
+
12
+ from urllib3.exceptions import HTTPError
13
+
14
+ from scout_apm.compat import urllib3_cert_pool_manager
15
+ from scout_apm.core.config import scout_config
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ CA_ALREADY_RUNNING_EXIT_CODE = 3
20
+
21
+
22
+ class CoreAgentManager(object):
23
+ def __init__(self):
24
+ self.core_agent_bin_path = None
25
+ self.core_agent_bin_version = None
26
+ self.core_agent_dir = "{}/{}".format(
27
+ scout_config.value("core_agent_dir"),
28
+ scout_config.value("core_agent_full_name"),
29
+ )
30
+ self.downloader = CoreAgentDownloader(
31
+ self.core_agent_dir, scout_config.value("core_agent_full_name")
32
+ )
33
+
34
+ def launch(self):
35
+ if not scout_config.value("core_agent_launch"):
36
+ logger.debug(
37
+ "Not attempting to launch Core Agent "
38
+ "due to 'core_agent_launch' setting."
39
+ )
40
+ return False
41
+
42
+ if not self.verify():
43
+ if not scout_config.value("core_agent_download"):
44
+ logger.debug(
45
+ "Not attempting to download Core Agent due "
46
+ "to 'core_agent_download' setting."
47
+ )
48
+ return False
49
+
50
+ self.download()
51
+
52
+ if not self.verify():
53
+ logger.debug("Failed to verify Core Agent. Not launching Core Agent.")
54
+ return False
55
+
56
+ return self.run()
57
+
58
+ def download(self):
59
+ self.downloader.download()
60
+
61
+ def run(self):
62
+ try:
63
+ with open(os.devnull) as devnull:
64
+ subprocess.check_call(
65
+ (
66
+ self.agent_binary()
67
+ + self.daemonize_flag()
68
+ + self.log_level()
69
+ + self.log_file()
70
+ + self.config_file()
71
+ + self.socket_path()
72
+ ),
73
+ close_fds=True,
74
+ stdout=devnull,
75
+ )
76
+ except subprocess.CalledProcessError as err:
77
+ if err.returncode == CA_ALREADY_RUNNING_EXIT_CODE:
78
+ # Other processes may have already started the core agent.
79
+ logger.debug("Core agent already running.")
80
+ return True
81
+ else:
82
+ logger.exception("CalledProcessError running Core Agent")
83
+ return False
84
+ except Exception:
85
+ # TODO detect failure of launch properly
86
+ logger.exception("Error running Core Agent")
87
+ return False
88
+ return True
89
+
90
+ def agent_binary(self):
91
+ return [self.core_agent_bin_path, "start"]
92
+
93
+ def daemonize_flag(self):
94
+ return ["--daemonize", "true"]
95
+
96
+ def socket_path(self):
97
+ path = get_socket_path()
98
+ if path.is_tcp:
99
+ return ["--tcp", path.tcp_address]
100
+ else:
101
+ return ["--socket", path]
102
+
103
+ def log_level(self):
104
+ # Old deprecated name "log_level"
105
+ log_level = scout_config.value("log_level")
106
+ if log_level is None:
107
+ log_level = scout_config.value("core_agent_log_level")
108
+ return ["--log-level", log_level]
109
+
110
+ def log_file(self):
111
+ # Old deprecated name "log_file"
112
+ path = scout_config.value("log_file")
113
+ if path is None:
114
+ path = scout_config.value("core_agent_log_file")
115
+
116
+ if path is not None:
117
+ return ["--log-file", path]
118
+ else:
119
+ return []
120
+
121
+ def config_file(self):
122
+ # Old deprecated name "config_file"
123
+ path = scout_config.value("config_file")
124
+ if path is None:
125
+ path = scout_config.value("core_agent_config_file")
126
+
127
+ if path is not None:
128
+ return ["--config-file", path]
129
+ else:
130
+ return []
131
+
132
+ def verify(self):
133
+ manifest = parse_manifest(self.core_agent_dir + "/manifest.json")
134
+ if manifest is None:
135
+ logger.debug(
136
+ "Core Agent verification failed: CoreAgentManifest is not valid."
137
+ )
138
+ self.core_agent_bin_path = None
139
+ self.core_agent_bin_version = None
140
+ return False
141
+
142
+ bin_path = os.path.join(self.core_agent_dir, manifest.bin_name)
143
+ if sha256_digest(bin_path) == manifest.sha256:
144
+ self.core_agent_bin_path = bin_path
145
+ self.core_agent_bin_version = manifest.bin_version
146
+ return True
147
+ else:
148
+ logger.debug("Core Agent verification failed: SHA mismatch.")
149
+ self.core_agent_bin_path = None
150
+ self.core_agent_bin_version = None
151
+ return False
152
+
153
+
154
+ class CoreAgentDownloader(object):
155
+ def __init__(self, download_destination, core_agent_full_name):
156
+ self.stale_download_secs = 120
157
+ self.destination = download_destination
158
+ self.core_agent_full_name = core_agent_full_name
159
+ self.package_location = self.destination + "/{}.tgz".format(
160
+ self.core_agent_full_name
161
+ )
162
+ self.download_lock_path = self.destination + "/download.lock"
163
+ self.download_lock_fd = None
164
+
165
+ def download(self):
166
+ self.create_core_agent_dir()
167
+ self.obtain_download_lock()
168
+ if self.download_lock_fd is not None:
169
+ try:
170
+ downloaded = self.download_package()
171
+ if downloaded:
172
+ self.untar()
173
+ else:
174
+ logger.debug("Core Agent download failed: bad http response.")
175
+ except (OSError, HTTPError):
176
+ logger.exception("Exception raised while downloading Core Agent")
177
+ finally:
178
+ self.release_download_lock()
179
+
180
+ def create_core_agent_dir(self):
181
+ try:
182
+ os.makedirs(self.destination, scout_config.core_agent_permissions())
183
+ except OSError:
184
+ pass
185
+
186
+ def obtain_download_lock(self):
187
+ self.clean_stale_download_lock()
188
+ try:
189
+ self.download_lock_fd = os.open(
190
+ self.download_lock_path,
191
+ os.O_RDWR | os.O_CREAT | os.O_EXCL | os.O_NONBLOCK,
192
+ )
193
+ except OSError as exc:
194
+ logger.debug(
195
+ "Could not obtain download lock on %s",
196
+ self.download_lock_path,
197
+ exc_info=exc,
198
+ )
199
+ self.download_lock_fd = None
200
+
201
+ def clean_stale_download_lock(self):
202
+ try:
203
+ delta = time.time() - os.stat(self.download_lock_path).st_ctime
204
+ if delta > self.stale_download_secs:
205
+ logger.debug("Clearing stale download lock file.")
206
+ os.unlink(self.download_lock_path)
207
+ except OSError:
208
+ pass
209
+
210
+ def release_download_lock(self):
211
+ if self.download_lock_fd is not None:
212
+ os.unlink(self.download_lock_path)
213
+ os.close(self.download_lock_fd)
214
+
215
+ def download_package(self):
216
+ full_url = self.full_url()
217
+ logger.debug("Downloading: %s to %s", full_url, self.package_location)
218
+ http = urllib3_cert_pool_manager()
219
+ response = http.request(
220
+ "GET", full_url, preload_content=False, timeout=10.0, retries=3
221
+ )
222
+ try:
223
+ if response.status != 200:
224
+ return False
225
+ with open(self.package_location, "wb") as fp:
226
+ for chunk in response.stream():
227
+ fp.write(chunk)
228
+ finally:
229
+ response.release_conn()
230
+ http.clear()
231
+ return True
232
+
233
+ def untar(self):
234
+ t = tarfile.open(self.package_location, "r")
235
+ t.extractall(self.destination)
236
+
237
+ def full_url(self):
238
+ return "{root_url}/{core_agent_full_name}.tgz".format(
239
+ root_url=self.root_url(), core_agent_full_name=self.core_agent_full_name
240
+ )
241
+
242
+ def root_url(self):
243
+ return scout_config.value("download_url")
244
+
245
+
246
+ def parse_manifest(path):
247
+ try:
248
+ manifest_file = open(path)
249
+ except OSError as exc:
250
+ if exc.errno == errno.ENOENT:
251
+ logger.debug("Core Agent Manifest does not exist at %s", path)
252
+ else:
253
+ logger.debug("Error opening Core Agent Manifest at %s", path, exc_info=exc)
254
+ return None
255
+
256
+ try:
257
+ with manifest_file:
258
+ data = json.load(manifest_file)
259
+ logger.debug("Core Agent manifest json: %s", data)
260
+
261
+ bin_name = data["core_agent_binary"]
262
+ if not isinstance(bin_name, str):
263
+ raise TypeError("core_agent_binary should be a string.")
264
+ bin_version = data["core_agent_version"]
265
+ if not isinstance(bin_version, str):
266
+ raise TypeError("core_agent_version should be a string.")
267
+ sha256 = data["core_agent_binary_sha256"]
268
+ if not isinstance(sha256, str):
269
+ raise TypeError("core_agent_binary_sha256 should be a string.")
270
+
271
+ return CoreAgentManifest(
272
+ bin_name=bin_name,
273
+ bin_version=bin_version,
274
+ sha256=sha256,
275
+ )
276
+
277
+ # IOError => OSError on Python 3
278
+ except (KeyError, ValueError, TypeError, OSError, IOError) as exc: # noqa: B014
279
+ logger.debug("Error parsing Core Agent Manifest", exc_info=exc)
280
+ return None
281
+
282
+
283
+ class CoreAgentManifest(object):
284
+ __slots__ = ("bin_name", "bin_version", "sha256")
285
+
286
+ def __init__(self, bin_name, bin_version, sha256):
287
+ self.bin_name = bin_name
288
+ self.bin_version = bin_version
289
+ self.sha256 = sha256
290
+
291
+
292
+ def sha256_digest(filename, block_size=65536):
293
+ try:
294
+ sha256 = hashlib.sha256()
295
+ with open(filename, "rb") as f:
296
+ for block in iter(lambda: f.read(block_size), b""):
297
+ sha256.update(block)
298
+ return sha256.hexdigest()
299
+ except OSError as exc:
300
+ logger.debug("Error on digest", exc_info=exc)
301
+ return None
302
+
303
+
304
+ class SocketPath(str):
305
+ @property
306
+ def is_tcp(self):
307
+ return self.startswith("tcp://")
308
+
309
+ @property
310
+ def tcp_address(self):
311
+ return self[len("tcp://") :]
312
+
313
+
314
+ def get_socket_path():
315
+ # Old deprecated name "socket_path"
316
+ socket_path = scout_config.value("socket_path")
317
+ if socket_path is None:
318
+ socket_path = scout_config.value("core_agent_socket_path")
319
+ return SocketPath(socket_path)