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.
- scout_apm/__init__.py +0 -0
- scout_apm/api/__init__.py +197 -0
- scout_apm/async_/__init__.py +1 -0
- scout_apm/async_/api.py +41 -0
- scout_apm/async_/instruments/__init__.py +0 -0
- scout_apm/async_/instruments/jinja2.py +13 -0
- scout_apm/async_/starlette.py +101 -0
- scout_apm/bottle.py +86 -0
- scout_apm/celery.py +153 -0
- scout_apm/compat.py +104 -0
- scout_apm/core/__init__.py +99 -0
- scout_apm/core/_objtrace.cpython-38-i386-linux-gnu.so +0 -0
- scout_apm/core/agent/__init__.py +0 -0
- scout_apm/core/agent/commands.py +250 -0
- scout_apm/core/agent/manager.py +319 -0
- scout_apm/core/agent/socket.py +211 -0
- scout_apm/core/backtrace.py +116 -0
- scout_apm/core/cli/__init__.py +0 -0
- scout_apm/core/cli/core_agent_manager.py +32 -0
- scout_apm/core/config.py +404 -0
- scout_apm/core/context.py +140 -0
- scout_apm/core/error.py +95 -0
- scout_apm/core/error_service.py +167 -0
- scout_apm/core/metadata.py +66 -0
- scout_apm/core/n_plus_one_tracker.py +41 -0
- scout_apm/core/objtrace.py +24 -0
- scout_apm/core/platform_detection.py +66 -0
- scout_apm/core/queue_time.py +99 -0
- scout_apm/core/sampler.py +149 -0
- scout_apm/core/samplers/__init__.py +0 -0
- scout_apm/core/samplers/cpu.py +76 -0
- scout_apm/core/samplers/memory.py +23 -0
- scout_apm/core/samplers/thread.py +41 -0
- scout_apm/core/stacktracer.py +30 -0
- scout_apm/core/threading.py +56 -0
- scout_apm/core/tracked_request.py +328 -0
- scout_apm/core/web_requests.py +167 -0
- scout_apm/django/__init__.py +7 -0
- scout_apm/django/apps.py +137 -0
- scout_apm/django/instruments/__init__.py +0 -0
- scout_apm/django/instruments/huey.py +30 -0
- scout_apm/django/instruments/sql.py +140 -0
- scout_apm/django/instruments/template.py +35 -0
- scout_apm/django/middleware.py +211 -0
- scout_apm/django/request.py +144 -0
- scout_apm/dramatiq.py +42 -0
- scout_apm/falcon.py +142 -0
- scout_apm/flask/__init__.py +118 -0
- scout_apm/flask/sqlalchemy.py +28 -0
- scout_apm/huey.py +54 -0
- scout_apm/hug.py +40 -0
- scout_apm/instruments/__init__.py +21 -0
- scout_apm/instruments/elasticsearch.py +263 -0
- scout_apm/instruments/jinja2.py +127 -0
- scout_apm/instruments/pymongo.py +105 -0
- scout_apm/instruments/redis.py +77 -0
- scout_apm/instruments/urllib3.py +80 -0
- scout_apm/rq.py +85 -0
- scout_apm/sqlalchemy.py +38 -0
- scout_apm-3.3.0.dist-info/LICENSE +21 -0
- scout_apm-3.3.0.dist-info/METADATA +82 -0
- scout_apm-3.3.0.dist-info/RECORD +65 -0
- scout_apm-3.3.0.dist-info/WHEEL +5 -0
- scout_apm-3.3.0.dist-info/entry_points.txt +2 -0
- scout_apm-3.3.0.dist-info/top_level.txt +1 -0
scout_apm/dramatiq.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import dramatiq
|
4
|
+
|
5
|
+
import scout_apm.core
|
6
|
+
from scout_apm.core.tracked_request import TrackedRequest
|
7
|
+
|
8
|
+
|
9
|
+
class ScoutMiddleware(dramatiq.Middleware):
|
10
|
+
def __init__(self):
|
11
|
+
installed = scout_apm.core.install()
|
12
|
+
self._do_nothing = not installed
|
13
|
+
|
14
|
+
def before_process_message(self, broker, message):
|
15
|
+
if self._do_nothing:
|
16
|
+
return
|
17
|
+
tracked_request = TrackedRequest.instance()
|
18
|
+
tracked_request.tag("queue", message.queue_name)
|
19
|
+
tracked_request.tag("message_id", message.message_id)
|
20
|
+
operation = "Job/" + message.actor_name
|
21
|
+
tracked_request.start_span(operation=operation)
|
22
|
+
tracked_request.operation = operation
|
23
|
+
|
24
|
+
def after_process_message(self, broker, message, result=None, exception=None):
|
25
|
+
if self._do_nothing:
|
26
|
+
return
|
27
|
+
tracked_request = TrackedRequest.instance()
|
28
|
+
tracked_request.is_real_request = True
|
29
|
+
if exception:
|
30
|
+
tracked_request.tag("error", "true")
|
31
|
+
tracked_request.stop_span()
|
32
|
+
|
33
|
+
def after_skip_message(self, broker, message):
|
34
|
+
"""
|
35
|
+
The message was skipped by another middleware raising SkipMessage.
|
36
|
+
Stop the span and thus the request, it won't have been marked as real
|
37
|
+
so that's alright.
|
38
|
+
"""
|
39
|
+
if self._do_nothing:
|
40
|
+
return
|
41
|
+
tracked_request = TrackedRequest.instance()
|
42
|
+
tracked_request.stop_span()
|
scout_apm/falcon.py
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import warnings
|
5
|
+
|
6
|
+
import falcon
|
7
|
+
|
8
|
+
from scout_apm.api import install
|
9
|
+
from scout_apm.core.config import scout_config
|
10
|
+
from scout_apm.core.queue_time import track_request_queue_time
|
11
|
+
from scout_apm.core.tracked_request import TrackedRequest
|
12
|
+
from scout_apm.core.web_requests import create_filtered_path, ignore_path
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
# Falcon Middleware docs:
|
17
|
+
# https://falcon.readthedocs.io/en/stable/api/middleware.html
|
18
|
+
|
19
|
+
|
20
|
+
class ScoutMiddleware(object):
|
21
|
+
"""
|
22
|
+
Falcon Middleware for integration with Scout APM.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, config, hug_http_interface=None):
|
26
|
+
self.api = None
|
27
|
+
self.hug_http_interface = hug_http_interface
|
28
|
+
installed = install(config=config)
|
29
|
+
self._do_nothing = not installed
|
30
|
+
|
31
|
+
def set_api(self, api):
|
32
|
+
if not isinstance(api, falcon.API):
|
33
|
+
raise ValueError("api should be an instance of falcon.API")
|
34
|
+
self.api = api
|
35
|
+
|
36
|
+
def process_request(self, req, resp):
|
37
|
+
if self._do_nothing:
|
38
|
+
return
|
39
|
+
if self.api is None and self.hug_http_interface is not None:
|
40
|
+
self.api = self.hug_http_interface.falcon
|
41
|
+
tracked_request = TrackedRequest.instance()
|
42
|
+
tracked_request.is_real_request = True
|
43
|
+
req.context.scout_tracked_request = tracked_request
|
44
|
+
tracked_request.start_span(
|
45
|
+
operation="Middleware", should_capture_backtrace=False
|
46
|
+
)
|
47
|
+
|
48
|
+
path = req.path
|
49
|
+
# Falcon URL parameter values are *either* single items or lists
|
50
|
+
url_params = [
|
51
|
+
(k, v)
|
52
|
+
for k, vs in req.params.items()
|
53
|
+
for v in (vs if isinstance(vs, list) else [vs])
|
54
|
+
]
|
55
|
+
tracked_request.tag("path", create_filtered_path(path, url_params))
|
56
|
+
if ignore_path(path):
|
57
|
+
tracked_request.tag("ignore_transaction", True)
|
58
|
+
|
59
|
+
if scout_config.value("collect_remote_ip"):
|
60
|
+
# Determine a remote IP to associate with the request. The value is
|
61
|
+
# spoofable by the requester so this is not suitable to use in any
|
62
|
+
# security sensitive context.
|
63
|
+
user_ip = (
|
64
|
+
req.get_header("x-forwarded-for", default="").split(",")[0]
|
65
|
+
or req.get_header("client-ip", default="").split(",")[0]
|
66
|
+
or req.remote_addr
|
67
|
+
)
|
68
|
+
tracked_request.tag("user_ip", user_ip)
|
69
|
+
|
70
|
+
queue_time = req.get_header("x-queue-start", default="") or req.get_header(
|
71
|
+
"x-request-start", default=""
|
72
|
+
)
|
73
|
+
track_request_queue_time(queue_time, tracked_request)
|
74
|
+
|
75
|
+
def process_resource(self, req, resp, resource, params):
|
76
|
+
if self._do_nothing:
|
77
|
+
return
|
78
|
+
|
79
|
+
tracked_request = getattr(req.context, "scout_tracked_request", None)
|
80
|
+
if tracked_request is None:
|
81
|
+
# Somehow we didn't start a request - this might occur in
|
82
|
+
# combination with a pretty adversarial application, so guard
|
83
|
+
# against it, although if a request was started and the context was
|
84
|
+
# lost, other problems might occur.
|
85
|
+
return
|
86
|
+
|
87
|
+
if self.api is None:
|
88
|
+
warnings.warn(
|
89
|
+
(
|
90
|
+
"{}.set_api() should be called before requests begin for"
|
91
|
+
+ " more detail."
|
92
|
+
).format(self.__class__.__name__),
|
93
|
+
RuntimeWarning,
|
94
|
+
stacklevel=2,
|
95
|
+
)
|
96
|
+
operation = "Controller/{}.{}.{}".format(
|
97
|
+
resource.__module__, resource.__class__.__name__, req.method
|
98
|
+
)
|
99
|
+
else:
|
100
|
+
# Find the current responder's name. Falcon passes middleware the
|
101
|
+
# current resource but unfortunately not the method being called, hence
|
102
|
+
# we have to go through routing again.
|
103
|
+
responder, _params, _resource, _uri_template = self.api._get_responder(req)
|
104
|
+
operation = self._name_operation(req, responder, resource)
|
105
|
+
|
106
|
+
span = tracked_request.start_span(
|
107
|
+
operation=operation, should_capture_backtrace=False
|
108
|
+
)
|
109
|
+
tracked_request.operation = operation
|
110
|
+
req.context.scout_resource_span = span
|
111
|
+
|
112
|
+
def _name_operation(self, req, responder, resource):
|
113
|
+
try:
|
114
|
+
last_part = responder.__name__
|
115
|
+
except AttributeError:
|
116
|
+
last_part = req.method
|
117
|
+
return "Controller/{}.{}.{}".format(
|
118
|
+
resource.__module__, resource.__class__.__name__, last_part
|
119
|
+
)
|
120
|
+
|
121
|
+
def process_response(self, req, resp, resource, req_succeeded):
|
122
|
+
tracked_request = getattr(req.context, "scout_tracked_request", None)
|
123
|
+
if tracked_request is None:
|
124
|
+
# Somehow we didn't start a request
|
125
|
+
return
|
126
|
+
|
127
|
+
# Falcon only stores the response status line, we have to parse it
|
128
|
+
try:
|
129
|
+
status_code = int(resp.status.split(" ")[0])
|
130
|
+
except ValueError:
|
131
|
+
# Bad status line - force it to be tagged as an error because
|
132
|
+
# client will experience it as one
|
133
|
+
status_code = 500
|
134
|
+
|
135
|
+
if not req_succeeded or 500 <= status_code <= 599:
|
136
|
+
tracked_request.tag("error", "true")
|
137
|
+
|
138
|
+
span = getattr(req.context, "scout_resource_span", None)
|
139
|
+
if span is not None:
|
140
|
+
tracked_request.stop_span()
|
141
|
+
# Stop Middleware span
|
142
|
+
tracked_request.stop_span()
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import sys
|
5
|
+
|
6
|
+
import wrapt
|
7
|
+
from flask import current_app
|
8
|
+
from flask.globals import request, session
|
9
|
+
|
10
|
+
import scout_apm.core
|
11
|
+
from scout_apm.core.config import scout_config
|
12
|
+
from scout_apm.core.error import ErrorMonitor
|
13
|
+
from scout_apm.core.tracked_request import TrackedRequest
|
14
|
+
from scout_apm.core.web_requests import RequestComponents, werkzeug_track_request_data
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
19
|
+
class ScoutApm(object):
|
20
|
+
def __init__(self, app):
|
21
|
+
self.app = app
|
22
|
+
self._attempted_install = False
|
23
|
+
app.full_dispatch_request = self.wrapped_full_dispatch_request(
|
24
|
+
app.full_dispatch_request
|
25
|
+
)
|
26
|
+
app.preprocess_request = self.wrapped_preprocess_request(app.preprocess_request)
|
27
|
+
|
28
|
+
@wrapt.decorator
|
29
|
+
def wrapped_full_dispatch_request(self, wrapped, instance, args, kwargs):
|
30
|
+
if not self._attempted_install:
|
31
|
+
self.extract_flask_settings()
|
32
|
+
installed = scout_apm.core.install()
|
33
|
+
self._do_nothing = not installed
|
34
|
+
self._attempted_install = True
|
35
|
+
|
36
|
+
if self._do_nothing:
|
37
|
+
return wrapped(*args, **kwargs)
|
38
|
+
|
39
|
+
# Pass on routing exceptions (normally 404's)
|
40
|
+
if request.routing_exception is not None:
|
41
|
+
return wrapped(*args, **kwargs)
|
42
|
+
|
43
|
+
request_components = get_request_components(self.app, request)
|
44
|
+
operation = "Controller/{}.{}".format(
|
45
|
+
request_components.module, request_components.controller
|
46
|
+
)
|
47
|
+
|
48
|
+
tracked_request = TrackedRequest.instance()
|
49
|
+
tracked_request.is_real_request = True
|
50
|
+
tracked_request.operation = operation
|
51
|
+
request._scout_tracked_request = tracked_request
|
52
|
+
|
53
|
+
werkzeug_track_request_data(request, tracked_request)
|
54
|
+
|
55
|
+
with tracked_request.span(
|
56
|
+
operation=operation, should_capture_backtrace=False
|
57
|
+
) as span:
|
58
|
+
request._scout_view_span = span
|
59
|
+
|
60
|
+
try:
|
61
|
+
response = wrapped(*args, **kwargs)
|
62
|
+
except Exception as exc:
|
63
|
+
tracked_request.tag("error", "true")
|
64
|
+
if scout_config.value("errors_enabled"):
|
65
|
+
ErrorMonitor.send(
|
66
|
+
sys.exc_info(),
|
67
|
+
request_components=get_request_components(self.app, request),
|
68
|
+
request_path=request.path,
|
69
|
+
request_params=dict(request.args.lists()),
|
70
|
+
session=dict(session.items()),
|
71
|
+
environment=self.app.config,
|
72
|
+
)
|
73
|
+
raise exc
|
74
|
+
else:
|
75
|
+
if 500 <= response.status_code <= 599:
|
76
|
+
tracked_request.tag("error", "true")
|
77
|
+
return response
|
78
|
+
|
79
|
+
@wrapt.decorator
|
80
|
+
def wrapped_preprocess_request(self, wrapped, instance, args, kwargs):
|
81
|
+
tracked_request = getattr(request, "_scout_tracked_request", None)
|
82
|
+
if tracked_request is None:
|
83
|
+
return wrapped(*args, **kwargs)
|
84
|
+
|
85
|
+
# Unlike middleware in other frameworks, using request preprocessors is
|
86
|
+
# less common in Flask, so only add a span if there is any in use
|
87
|
+
have_before_request_funcs = (
|
88
|
+
None in instance.before_request_funcs
|
89
|
+
or request.blueprint in instance.before_request_funcs
|
90
|
+
)
|
91
|
+
if not have_before_request_funcs:
|
92
|
+
return wrapped(*args, **kwargs)
|
93
|
+
|
94
|
+
with tracked_request.span("PreprocessRequest", should_capture_backtrace=False):
|
95
|
+
return wrapped(*args, **kwargs)
|
96
|
+
|
97
|
+
def extract_flask_settings(self):
|
98
|
+
"""
|
99
|
+
Copies SCOUT_* settings in the app into Scout's config lookup
|
100
|
+
"""
|
101
|
+
configs = {}
|
102
|
+
configs["application_root"] = self.app.instance_path
|
103
|
+
for name in current_app.config:
|
104
|
+
if name.startswith("SCOUT_"):
|
105
|
+
value = current_app.config[name]
|
106
|
+
clean_name = name.replace("SCOUT_", "").lower()
|
107
|
+
configs[clean_name] = value
|
108
|
+
scout_config.set(**configs)
|
109
|
+
|
110
|
+
|
111
|
+
def get_request_components(app, request):
|
112
|
+
view_func = app.view_functions[request.endpoint]
|
113
|
+
request_components = RequestComponents(
|
114
|
+
module=view_func.__module__,
|
115
|
+
controller=view_func.__name__,
|
116
|
+
action=request.method,
|
117
|
+
)
|
118
|
+
return request_components
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import wrapt
|
4
|
+
from flask_sqlalchemy import SQLAlchemy
|
5
|
+
|
6
|
+
import scout_apm.sqlalchemy
|
7
|
+
|
8
|
+
|
9
|
+
def instrument_sqlalchemy(db):
|
10
|
+
# Version 3 of flask_sqlalchemy changed how engines are created
|
11
|
+
if hasattr(db, "_make_engine"):
|
12
|
+
db._make_engine = wrapped_make_engine(db._make_engine)
|
13
|
+
else:
|
14
|
+
SQLAlchemy.get_engine = wrapped_get_engine(SQLAlchemy.get_engine)
|
15
|
+
|
16
|
+
|
17
|
+
@wrapt.decorator
|
18
|
+
def wrapped_get_engine(wrapped, instance, args, kwargs):
|
19
|
+
engine = wrapped(*args, **kwargs)
|
20
|
+
scout_apm.sqlalchemy.instrument_sqlalchemy(engine)
|
21
|
+
return engine
|
22
|
+
|
23
|
+
|
24
|
+
@wrapt.decorator
|
25
|
+
def wrapped_make_engine(wrapped, instance, args, kwargs):
|
26
|
+
engine = wrapped(*args, **kwargs)
|
27
|
+
scout_apm.sqlalchemy.instrument_sqlalchemy(engine)
|
28
|
+
return engine
|
scout_apm/huey.py
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
from huey.exceptions import RetryTask, TaskLockedException
|
4
|
+
from huey.signals import SIGNAL_CANCELED
|
5
|
+
|
6
|
+
import scout_apm.core
|
7
|
+
from scout_apm.core.tracked_request import TrackedRequest
|
8
|
+
|
9
|
+
# Because neither hooks nor signals are called in *all* cases, we need to use
|
10
|
+
# both in order to capture every case. See source:
|
11
|
+
# https://github.com/coleifer/huey/blob/e6710bd6a9f581ebc728e24f5923d26eb0047750/huey/api.py#L331 # noqa
|
12
|
+
|
13
|
+
|
14
|
+
def attach_scout(huey):
|
15
|
+
installed = scout_apm.core.install()
|
16
|
+
if installed:
|
17
|
+
attach_scout_handlers(huey)
|
18
|
+
|
19
|
+
|
20
|
+
def attach_scout_handlers(huey):
|
21
|
+
huey.pre_execute()(scout_on_pre_execute)
|
22
|
+
huey.post_execute()(scout_on_post_execute)
|
23
|
+
huey.signal(SIGNAL_CANCELED)(scout_on_cancelled)
|
24
|
+
|
25
|
+
|
26
|
+
def scout_on_pre_execute(task):
|
27
|
+
tracked_request = TrackedRequest.instance()
|
28
|
+
|
29
|
+
tracked_request.tag("task_id", task.id)
|
30
|
+
|
31
|
+
operation = "Job/{}.{}".format(task.__module__, task.__class__.__name__)
|
32
|
+
tracked_request.start_span(operation=operation)
|
33
|
+
tracked_request.operation = operation
|
34
|
+
|
35
|
+
|
36
|
+
def scout_on_post_execute(task, task_value, exception):
|
37
|
+
tracked_request = TrackedRequest.instance()
|
38
|
+
if exception is None:
|
39
|
+
tracked_request.is_real_request = True
|
40
|
+
elif isinstance(exception, TaskLockedException):
|
41
|
+
pass
|
42
|
+
elif isinstance(exception, RetryTask):
|
43
|
+
tracked_request.is_real_request = True
|
44
|
+
tracked_request.tag("retrying", True)
|
45
|
+
else:
|
46
|
+
tracked_request.is_real_request = True
|
47
|
+
tracked_request.tag("error", "true")
|
48
|
+
tracked_request.stop_span()
|
49
|
+
|
50
|
+
|
51
|
+
def scout_on_cancelled(signal, task, exc=None):
|
52
|
+
# In the case of a cancelled signal, Huey doesn't run the post_execute
|
53
|
+
# handler, so we need to tidy up
|
54
|
+
TrackedRequest.instance().stop_span()
|
scout_apm/hug.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import hug
|
4
|
+
from hug.interface import HTTP
|
5
|
+
|
6
|
+
from scout_apm.falcon import ScoutMiddleware as FalconMiddleware
|
7
|
+
|
8
|
+
|
9
|
+
class ScoutMiddleware(FalconMiddleware):
|
10
|
+
"""
|
11
|
+
Hug's HTTP interface is based on Falcon. Therefore we use a subclass of our
|
12
|
+
Falcon integration with Hug specific extras.
|
13
|
+
"""
|
14
|
+
|
15
|
+
def __init__(self, config, hug_http_interface):
|
16
|
+
super(ScoutMiddleware, self).__init__(config)
|
17
|
+
self.hug_http_interface = hug_http_interface
|
18
|
+
|
19
|
+
def process_request(self, req, resp):
|
20
|
+
if not self._do_nothing and self.api is None:
|
21
|
+
self.api = self.hug_http_interface.falcon
|
22
|
+
return super(ScoutMiddleware, self).process_request(req, resp)
|
23
|
+
|
24
|
+
def _name_operation(self, req, responder, resource):
|
25
|
+
if isinstance(responder, HTTP):
|
26
|
+
# Hug doesn't use functions but its custom callable classes
|
27
|
+
return "Controller/{}.{}".format(
|
28
|
+
responder.interface._function.__module__,
|
29
|
+
responder.interface._function.__name__,
|
30
|
+
)
|
31
|
+
return super(ScoutMiddleware, self)._name_operation(req, responder, resource)
|
32
|
+
|
33
|
+
|
34
|
+
def integrate_scout(hug_module_name, config):
|
35
|
+
http_interface = hug.API(hug_module_name).http
|
36
|
+
scout_middleware = ScoutMiddleware(
|
37
|
+
config=config,
|
38
|
+
hug_http_interface=http_interface,
|
39
|
+
)
|
40
|
+
http_interface.add_middleware(scout_middleware)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding=utf-8
|
2
|
+
|
3
|
+
import importlib
|
4
|
+
import logging
|
5
|
+
|
6
|
+
from scout_apm.core.config import scout_config
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
instrument_names = ["elasticsearch", "jinja2", "pymongo", "redis", "urllib3"]
|
11
|
+
|
12
|
+
|
13
|
+
def ensure_all_installed():
|
14
|
+
disabled_instruments = scout_config.value("disabled_instruments")
|
15
|
+
for instrument_name in instrument_names:
|
16
|
+
if instrument_name in disabled_instruments:
|
17
|
+
logger.info("%s instrument is disabled. Skipping.", instrument_name)
|
18
|
+
continue
|
19
|
+
|
20
|
+
module = importlib.import_module("{}.{}".format(__name__, instrument_name))
|
21
|
+
module.ensure_installed()
|