ob-metaflow 2.14.0.1__py2.py3-none-any.whl → 2.14.2.1__py2.py3-none-any.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.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/cli.py +0 -23
- metaflow/cli_components/run_cmds.py +34 -14
- metaflow/cli_components/step_cmd.py +2 -0
- metaflow/client/core.py +241 -1
- metaflow/cmd/main_cli.py +1 -1
- metaflow/metadata_provider/heartbeat.py +1 -0
- metaflow/metadata_provider/metadata.py +33 -0
- metaflow/metaflow_config.py +5 -9
- metaflow/mflog/save_logs.py +2 -2
- metaflow/plugins/argo/argo_workflows.py +12 -8
- metaflow/plugins/argo/argo_workflows_cli.py +2 -2
- metaflow/plugins/datatools/s3/s3op.py +4 -4
- metaflow/plugins/env_escape/server.py +7 -0
- metaflow/plugins/env_escape/stub.py +21 -4
- metaflow/plugins/kubernetes/kubernetes_cli.py +1 -1
- metaflow/plugins/metadata_providers/local.py +66 -0
- metaflow/plugins/metadata_providers/service.py +51 -0
- metaflow/plugins/pypi/bootstrap.py +4 -4
- metaflow/runner/click_api.py +6 -3
- metaflow/sidecar/sidecar_worker.py +1 -1
- metaflow/task.py +21 -2
- metaflow/tracing/__init__.py +7 -7
- metaflow/tracing/span_exporter.py +31 -38
- metaflow/tracing/tracing_modules.py +35 -43
- metaflow/version.py +1 -1
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/METADATA +2 -2
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/RECORD +31 -31
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/LICENSE +0 -0
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/top_level.txt +0 -0
|
@@ -276,9 +276,22 @@ class MetaWithConnection(StubMetaClass):
|
|
|
276
276
|
if len(args) > 0 and id(args[0]) == id(cls.___class_connection___):
|
|
277
277
|
return super(MetaWithConnection, cls).__call__(*args, **kwargs)
|
|
278
278
|
else:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
if hasattr(cls, "__overriden_init__"):
|
|
280
|
+
return cls.__overriden_init__(
|
|
281
|
+
None,
|
|
282
|
+
functools.partial(
|
|
283
|
+
cls.___class_connection___.stub_request,
|
|
284
|
+
None,
|
|
285
|
+
OP_INIT,
|
|
286
|
+
cls.___class_remote_class_name___,
|
|
287
|
+
),
|
|
288
|
+
*args,
|
|
289
|
+
**kwargs
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
return cls.___class_connection___.stub_request(
|
|
293
|
+
None, OP_INIT, cls.___class_remote_class_name___, *args, **kwargs
|
|
294
|
+
)
|
|
282
295
|
|
|
283
296
|
def __subclasscheck__(cls, subclass):
|
|
284
297
|
subclass_name = "%s.%s" % (subclass.__module__, subclass.__name__)
|
|
@@ -381,7 +394,10 @@ def create_class(
|
|
|
381
394
|
name = name[7:]
|
|
382
395
|
method_type = CLASS_METHOD
|
|
383
396
|
if name in overriden_methods:
|
|
384
|
-
if
|
|
397
|
+
if name == "__init__":
|
|
398
|
+
class_dict["__overriden_init__"] = overriden_methods["__init__"]
|
|
399
|
+
|
|
400
|
+
elif method_type == NORMAL_METHOD:
|
|
385
401
|
class_dict[name] = (
|
|
386
402
|
lambda override, orig_method: lambda obj, *args, **kwargs: override(
|
|
387
403
|
obj, functools.partial(orig_method, obj), *args, **kwargs
|
|
@@ -412,6 +428,7 @@ def create_class(
|
|
|
412
428
|
class_dict[name] = _make_method(
|
|
413
429
|
method_type, connection, class_name, name, doc
|
|
414
430
|
)
|
|
431
|
+
|
|
415
432
|
# Check for any getattr/setattr overrides
|
|
416
433
|
special_attributes = set(getattr_overrides.keys())
|
|
417
434
|
special_attributes.update(set(setattr_overrides.keys()))
|
|
@@ -39,7 +39,7 @@ def kubernetes():
|
|
|
39
39
|
"command inside a Kubernetes pod with the given options. Typically you do not call "
|
|
40
40
|
"this command directly; it is used internally by Metaflow."
|
|
41
41
|
)
|
|
42
|
-
@tracing.
|
|
42
|
+
@tracing.cli("kubernetes/step")
|
|
43
43
|
@click.argument("step-name")
|
|
44
44
|
@click.argument("code-package-sha")
|
|
45
45
|
@click.argument("code-package-url")
|
|
@@ -2,10 +2,12 @@ import collections
|
|
|
2
2
|
import glob
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import random
|
|
6
7
|
import tempfile
|
|
7
8
|
import time
|
|
8
9
|
from collections import namedtuple
|
|
10
|
+
from typing import List
|
|
9
11
|
|
|
10
12
|
from metaflow.exception import MetaflowInternalError, MetaflowTaggingError
|
|
11
13
|
from metaflow.metadata_provider.metadata import ObjectOrder
|
|
@@ -202,6 +204,70 @@ class LocalMetadataProvider(MetadataProvider):
|
|
|
202
204
|
"Tagging failed due to too many conflicting updates from other processes"
|
|
203
205
|
)
|
|
204
206
|
|
|
207
|
+
@classmethod
|
|
208
|
+
def filter_tasks_by_metadata(
|
|
209
|
+
cls,
|
|
210
|
+
flow_name: str,
|
|
211
|
+
run_id: str,
|
|
212
|
+
step_name: str,
|
|
213
|
+
field_name: str,
|
|
214
|
+
pattern: str,
|
|
215
|
+
) -> List[str]:
|
|
216
|
+
"""
|
|
217
|
+
Filter tasks by metadata field and pattern, returning task pathspecs that match criteria.
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
flow_name : str
|
|
222
|
+
Identifier for the flow
|
|
223
|
+
run_id : str
|
|
224
|
+
Identifier for the run
|
|
225
|
+
step_name : str
|
|
226
|
+
Name of the step to query tasks from
|
|
227
|
+
field_name : str
|
|
228
|
+
Name of metadata field to query
|
|
229
|
+
pattern : str
|
|
230
|
+
Pattern to match in metadata field value
|
|
231
|
+
|
|
232
|
+
Returns
|
|
233
|
+
-------
|
|
234
|
+
List[str]
|
|
235
|
+
List of task pathspecs that match the query criteria
|
|
236
|
+
"""
|
|
237
|
+
tasks = cls.get_object("step", "task", {}, None, flow_name, run_id, step_name)
|
|
238
|
+
if not tasks:
|
|
239
|
+
return []
|
|
240
|
+
|
|
241
|
+
regex = re.compile(pattern)
|
|
242
|
+
matching_task_pathspecs = []
|
|
243
|
+
|
|
244
|
+
for task in tasks:
|
|
245
|
+
task_id = task.get("task_id")
|
|
246
|
+
if not task_id:
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
if pattern == ".*":
|
|
250
|
+
# If the pattern is ".*", we can match all tasks without reading metadata
|
|
251
|
+
matching_task_pathspecs.append(
|
|
252
|
+
f"{flow_name}/{run_id}/{step_name}/{task_id}"
|
|
253
|
+
)
|
|
254
|
+
continue
|
|
255
|
+
|
|
256
|
+
metadata = cls.get_object(
|
|
257
|
+
"task", "metadata", {}, None, flow_name, run_id, step_name, task_id
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if any(
|
|
261
|
+
meta.get("field_name") == field_name
|
|
262
|
+
and regex.match(meta.get("value", ""))
|
|
263
|
+
for meta in metadata
|
|
264
|
+
):
|
|
265
|
+
matching_task_pathspecs.append(
|
|
266
|
+
f"{flow_name}/{run_id}/{step_name}/{task_id}"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return matching_task_pathspecs
|
|
270
|
+
|
|
205
271
|
@classmethod
|
|
206
272
|
def _get_object_internal(
|
|
207
273
|
cls, obj_type, obj_order, sub_type, sub_order, filters, attempt, *args
|
|
@@ -4,6 +4,7 @@ import time
|
|
|
4
4
|
|
|
5
5
|
import requests
|
|
6
6
|
|
|
7
|
+
from typing import List
|
|
7
8
|
from metaflow.exception import (
|
|
8
9
|
MetaflowException,
|
|
9
10
|
MetaflowInternalError,
|
|
@@ -13,6 +14,7 @@ from metaflow.metadata_provider import MetadataProvider
|
|
|
13
14
|
from metaflow.metadata_provider.heartbeat import HB_URL_KEY
|
|
14
15
|
from metaflow.metaflow_config import SERVICE_HEADERS, SERVICE_RETRY_COUNT, SERVICE_URL
|
|
15
16
|
from metaflow.sidecar import Message, MessageTypes, Sidecar
|
|
17
|
+
from urllib.parse import urlencode
|
|
16
18
|
from metaflow.util import version_parse
|
|
17
19
|
|
|
18
20
|
|
|
@@ -318,6 +320,55 @@ class ServiceMetadataProvider(MetadataProvider):
|
|
|
318
320
|
self._register_system_metadata(run_id, step_name, task["task_id"], attempt)
|
|
319
321
|
return task["task_id"], did_create
|
|
320
322
|
|
|
323
|
+
@classmethod
|
|
324
|
+
def filter_tasks_by_metadata(
|
|
325
|
+
cls,
|
|
326
|
+
flow_name: str,
|
|
327
|
+
run_id: str,
|
|
328
|
+
step_name: str,
|
|
329
|
+
field_name: str,
|
|
330
|
+
pattern: str,
|
|
331
|
+
) -> List[str]:
|
|
332
|
+
"""
|
|
333
|
+
Filter tasks by metadata field and pattern, returning task pathspecs that match criteria.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
flow_name : str
|
|
338
|
+
Flow name, that the run belongs to.
|
|
339
|
+
run_id: str
|
|
340
|
+
Run id, together with flow_id, that identifies the specific Run whose tasks to query
|
|
341
|
+
step_name: str
|
|
342
|
+
Step name to query tasks from
|
|
343
|
+
field_name: str
|
|
344
|
+
Metadata field name to query
|
|
345
|
+
pattern: str
|
|
346
|
+
Pattern to match in metadata field value
|
|
347
|
+
|
|
348
|
+
Returns
|
|
349
|
+
-------
|
|
350
|
+
List[str]
|
|
351
|
+
List of task pathspecs that satisfy the query
|
|
352
|
+
"""
|
|
353
|
+
query_params = {
|
|
354
|
+
"metadata_field_name": field_name,
|
|
355
|
+
"pattern": pattern,
|
|
356
|
+
"step_name": step_name,
|
|
357
|
+
}
|
|
358
|
+
url = ServiceMetadataProvider._obj_path(flow_name, run_id, step_name)
|
|
359
|
+
url = f"{url}/filtered_tasks?{urlencode(query_params)}"
|
|
360
|
+
try:
|
|
361
|
+
resp = cls._request(None, url, "GET")
|
|
362
|
+
except Exception as e:
|
|
363
|
+
if e.http_code == 404:
|
|
364
|
+
# filter_tasks_by_metadata endpoint does not exist in the version of metadata service
|
|
365
|
+
# deployed currently. Raise a more informative error message.
|
|
366
|
+
raise MetaflowInternalError(
|
|
367
|
+
"The version of metadata service deployed currently does not support filtering tasks by metadata. "
|
|
368
|
+
"Upgrade Metadata service to version 2.15 or greater to use this feature."
|
|
369
|
+
) from e
|
|
370
|
+
return resp
|
|
371
|
+
|
|
321
372
|
@staticmethod
|
|
322
373
|
def _obj_path(
|
|
323
374
|
flow_name,
|
|
@@ -10,7 +10,7 @@ import tarfile
|
|
|
10
10
|
import time
|
|
11
11
|
from urllib.error import URLError
|
|
12
12
|
from urllib.request import urlopen
|
|
13
|
-
from metaflow.metaflow_config import DATASTORE_LOCAL_DIR
|
|
13
|
+
from metaflow.metaflow_config import DATASTORE_LOCAL_DIR
|
|
14
14
|
from metaflow.plugins import DATASTORES
|
|
15
15
|
from metaflow.plugins.pypi.utils import MICROMAMBA_MIRROR_URL, MICROMAMBA_URL
|
|
16
16
|
from metaflow.util import which
|
|
@@ -329,8 +329,6 @@ if __name__ == "__main__":
|
|
|
329
329
|
|
|
330
330
|
@timer
|
|
331
331
|
def fast_setup_environment(architecture, storage, env, prefix, pkgs_dir):
|
|
332
|
-
install_fast_initializer(architecture)
|
|
333
|
-
|
|
334
332
|
# Get package urls
|
|
335
333
|
conda_pkgs = env["conda"]
|
|
336
334
|
pypi_pkgs = env.get("pypi", [])
|
|
@@ -381,7 +379,9 @@ if __name__ == "__main__":
|
|
|
381
379
|
with open(os.path.join(manifest_dir, MAGIC_FILE)) as f:
|
|
382
380
|
env = json.load(f)[id_][architecture]
|
|
383
381
|
|
|
384
|
-
if
|
|
382
|
+
if datastore_type == "s3":
|
|
383
|
+
# TODO: Remove this once fast-initializer is ready for all datastores
|
|
384
|
+
install_fast_initializer(architecture)
|
|
385
385
|
fast_setup_environment(architecture, storage, env, prefix, pkgs_dir)
|
|
386
386
|
else:
|
|
387
387
|
setup_environment(
|
metaflow/runner/click_api.py
CHANGED
|
@@ -97,12 +97,13 @@ def _method_sanity_check(
|
|
|
97
97
|
check_type(supplied_v, annotations[supplied_k])
|
|
98
98
|
except TypeCheckError:
|
|
99
99
|
raise TypeError(
|
|
100
|
-
"Invalid type for '%s' (%s), expected: '%s', default is '%s'"
|
|
100
|
+
"Invalid type for '%s' (%s), expected: '%s', default is '%s' but found '%s'"
|
|
101
101
|
% (
|
|
102
102
|
supplied_k,
|
|
103
103
|
type(supplied_k),
|
|
104
104
|
annotations[supplied_k],
|
|
105
105
|
defaults[supplied_k],
|
|
106
|
+
str(supplied_v),
|
|
106
107
|
)
|
|
107
108
|
)
|
|
108
109
|
|
|
@@ -218,7 +219,7 @@ def get_inspect_param_obj(p: Union[click.Argument, click.Option], kind: str):
|
|
|
218
219
|
default=inspect.Parameter.empty if is_vararg else p.default,
|
|
219
220
|
annotation=annotation,
|
|
220
221
|
),
|
|
221
|
-
annotation,
|
|
222
|
+
Optional[TTuple[annotation]] if is_vararg else annotation,
|
|
222
223
|
)
|
|
223
224
|
|
|
224
225
|
|
|
@@ -392,7 +393,9 @@ class MetaflowAPI(object):
|
|
|
392
393
|
options = params.pop("options", {})
|
|
393
394
|
|
|
394
395
|
for _, v in args.items():
|
|
395
|
-
if
|
|
396
|
+
if v is None:
|
|
397
|
+
continue
|
|
398
|
+
if isinstance(v, (list, tuple)):
|
|
396
399
|
for i in v:
|
|
397
400
|
components.append(i)
|
|
398
401
|
else:
|
|
@@ -49,7 +49,7 @@ def process_messages(worker_type, worker):
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
@click.command(help="Initialize workers")
|
|
52
|
-
@tracing.
|
|
52
|
+
@tracing.cli("sidecar")
|
|
53
53
|
@click.argument("worker-type")
|
|
54
54
|
def main(worker_type):
|
|
55
55
|
sidecar_type = SIDECARS.get(worker_type)
|
metaflow/task.py
CHANGED
|
@@ -493,6 +493,25 @@ class MetaflowTask(object):
|
|
|
493
493
|
)
|
|
494
494
|
)
|
|
495
495
|
|
|
496
|
+
# Add runtime dag information to the metadata of the task
|
|
497
|
+
foreach_execution_path = ",".join(
|
|
498
|
+
[
|
|
499
|
+
"{}:{}".format(foreach_frame.step, foreach_frame.index)
|
|
500
|
+
for foreach_frame in foreach_stack
|
|
501
|
+
]
|
|
502
|
+
)
|
|
503
|
+
if foreach_execution_path:
|
|
504
|
+
metadata.extend(
|
|
505
|
+
[
|
|
506
|
+
MetaDatum(
|
|
507
|
+
field="foreach-execution-path",
|
|
508
|
+
value=foreach_execution_path,
|
|
509
|
+
type="foreach-execution-path",
|
|
510
|
+
tags=metadata_tags,
|
|
511
|
+
),
|
|
512
|
+
]
|
|
513
|
+
)
|
|
514
|
+
|
|
496
515
|
self.metadata.register_metadata(
|
|
497
516
|
run_id,
|
|
498
517
|
step_name,
|
|
@@ -559,6 +578,7 @@ class MetaflowTask(object):
|
|
|
559
578
|
self.flow._success = False
|
|
560
579
|
self.flow._task_ok = None
|
|
561
580
|
self.flow._exception = None
|
|
581
|
+
|
|
562
582
|
# Note: All internal flow attributes (ie: non-user artifacts)
|
|
563
583
|
# should either be set prior to running the user code or listed in
|
|
564
584
|
# FlowSpec._EPHEMERAL to allow for proper merging/importing of
|
|
@@ -616,7 +636,6 @@ class MetaflowTask(object):
|
|
|
616
636
|
"graph_info": self.flow._graph_info,
|
|
617
637
|
}
|
|
618
638
|
)
|
|
619
|
-
|
|
620
639
|
for deco in decorators:
|
|
621
640
|
deco.task_pre_step(
|
|
622
641
|
step_name,
|
|
@@ -728,7 +747,7 @@ class MetaflowTask(object):
|
|
|
728
747
|
value=attempt_ok,
|
|
729
748
|
type="internal_attempt_status",
|
|
730
749
|
tags=["attempt_id:{0}".format(retry_count)],
|
|
731
|
-
)
|
|
750
|
+
),
|
|
732
751
|
],
|
|
733
752
|
)
|
|
734
753
|
|
metaflow/tracing/__init__.py
CHANGED
|
@@ -20,15 +20,15 @@ def post_fork():
|
|
|
20
20
|
yield
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def
|
|
24
|
-
def
|
|
23
|
+
def cli(name: str):
|
|
24
|
+
def cli_wrap(func):
|
|
25
25
|
@wraps(func)
|
|
26
26
|
def wrapper_func(*args, **kwargs):
|
|
27
27
|
return func(*args, **kwargs)
|
|
28
28
|
|
|
29
29
|
return wrapper_func
|
|
30
30
|
|
|
31
|
-
return
|
|
31
|
+
return cli_wrap
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def inject_tracing_vars(env_dict: Dict[str, str]) -> Dict[str, str]:
|
|
@@ -40,7 +40,9 @@ def get_trace_id() -> str:
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@contextlib.contextmanager
|
|
43
|
-
def traced(name, attrs=
|
|
43
|
+
def traced(name, attrs=None):
|
|
44
|
+
if attrs is None:
|
|
45
|
+
attrs = {}
|
|
44
46
|
yield
|
|
45
47
|
|
|
46
48
|
|
|
@@ -54,17 +56,15 @@ def tracing(func):
|
|
|
54
56
|
|
|
55
57
|
if not DISABLE_TRACING and (CONSOLE_TRACE_ENABLED or OTEL_ENDPOINT or ZIPKIN_ENDPOINT):
|
|
56
58
|
try:
|
|
57
|
-
# Overrides No-Op implementations if a specific provider is configured.
|
|
58
59
|
from .tracing_modules import (
|
|
59
60
|
init_tracing,
|
|
60
61
|
post_fork,
|
|
61
|
-
|
|
62
|
+
cli,
|
|
62
63
|
inject_tracing_vars,
|
|
63
64
|
get_trace_id,
|
|
64
65
|
traced,
|
|
65
66
|
tracing,
|
|
66
67
|
)
|
|
67
|
-
|
|
68
68
|
except ImportError as e:
|
|
69
69
|
# We keep the errors silent by default so that having tracing environment variables present
|
|
70
70
|
# does not affect users with no need for tracing.
|
|
@@ -3,6 +3,8 @@ from metaflow.metaflow_config import (
|
|
|
3
3
|
OTEL_ENDPOINT,
|
|
4
4
|
ZIPKIN_ENDPOINT,
|
|
5
5
|
CONSOLE_TRACE_ENABLED,
|
|
6
|
+
SERVICE_AUTH_KEY,
|
|
7
|
+
SERVICE_HEADERS,
|
|
6
8
|
)
|
|
7
9
|
|
|
8
10
|
if OTEL_ENDPOINT:
|
|
@@ -11,56 +13,47 @@ if OTEL_ENDPOINT:
|
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
def get_span_exporter():
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
exporter_map = {
|
|
17
|
+
OTEL_ENDPOINT: _create_otel_exporter,
|
|
18
|
+
ZIPKIN_ENDPOINT: _create_zipkin_exporter,
|
|
19
|
+
CONSOLE_TRACE_ENABLED: _create_console_exporter,
|
|
20
|
+
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
for config, create_exporter in exporter_map.items():
|
|
23
|
+
if config:
|
|
24
|
+
return create_exporter()
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
else:
|
|
23
|
-
print("WARNING: endpoints not set up for Opentelemetry", file=sys.stderr)
|
|
24
|
-
return
|
|
26
|
+
print("WARNING: endpoints not set up for OpenTelemetry", file=sys.stderr)
|
|
27
|
+
return None
|
|
25
28
|
|
|
26
29
|
|
|
27
|
-
def
|
|
30
|
+
def _create_otel_exporter():
|
|
28
31
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
if not any([SERVICE_AUTH_KEY, SERVICE_HEADERS]):
|
|
34
|
+
print("WARNING: no auth settings for OpenTelemetry", file=sys.stderr)
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
config = {
|
|
38
|
+
"endpoint": OTEL_ENDPOINT,
|
|
39
|
+
"timeout": 15,
|
|
40
|
+
}
|
|
34
41
|
|
|
35
42
|
if SERVICE_AUTH_KEY:
|
|
36
|
-
|
|
37
|
-
endpoint=OTEL_ENDPOINT,
|
|
38
|
-
headers={"x-api-key": SERVICE_AUTH_KEY},
|
|
39
|
-
timeout=15,
|
|
40
|
-
)
|
|
43
|
+
config["headers"] = {"x-api-key": SERVICE_AUTH_KEY}
|
|
41
44
|
elif SERVICE_HEADERS:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
print("WARNING: no auth settings for Opentelemetry", file=sys.stderr)
|
|
49
|
-
return
|
|
50
|
-
return span_exporter
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def set_zipkin_exporter():
|
|
45
|
+
config["headers"] = SERVICE_HEADERS
|
|
46
|
+
|
|
47
|
+
return OTLPSpanExporter(**config)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _create_zipkin_exporter():
|
|
54
51
|
from opentelemetry.exporter.zipkin.proto.http import ZipkinExporter
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
endpoint=ZIPKIN_ENDPOINT,
|
|
58
|
-
)
|
|
59
|
-
return span_exporter
|
|
53
|
+
return ZipkinExporter(endpoint=ZIPKIN_ENDPOINT)
|
|
60
54
|
|
|
61
55
|
|
|
62
|
-
def
|
|
56
|
+
def _create_console_exporter():
|
|
63
57
|
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
return span_exporter
|
|
59
|
+
return ConsoleSpanExporter()
|
|
@@ -13,7 +13,7 @@ from typing import Dict, List, Optional
|
|
|
13
13
|
from opentelemetry import trace as trace_api, context
|
|
14
14
|
from .span_exporter import get_span_exporter
|
|
15
15
|
|
|
16
|
-
tracer_provider = None
|
|
16
|
+
tracer_provider: Optional[TracerProvider] = None
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def init_tracing():
|
|
@@ -22,23 +22,22 @@ def init_tracing():
|
|
|
22
22
|
import logging
|
|
23
23
|
logging.getLogger("opentelemetry").setLevel(logging.FATAL)
|
|
24
24
|
if tracer_provider is not None:
|
|
25
|
-
# print("Tracing already initialized", file=sys.stderr)
|
|
26
25
|
return
|
|
27
26
|
|
|
28
27
|
from .propagator import EnvPropagator
|
|
29
28
|
|
|
30
29
|
set_global_textmap(EnvPropagator(None))
|
|
31
|
-
span_exporter = get_span_exporter()
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
service_name = "metaflow-awsbatch"
|
|
37
|
-
else:
|
|
38
|
-
service_name = "metaflow-local"
|
|
31
|
+
span_exporter = get_span_exporter()
|
|
32
|
+
if span_exporter is None:
|
|
33
|
+
return
|
|
39
34
|
|
|
40
35
|
tracer_provider = TracerProvider(
|
|
41
|
-
resource=Resource.create(
|
|
36
|
+
resource=Resource.create(
|
|
37
|
+
{
|
|
38
|
+
SERVICE_NAME: "metaflow",
|
|
39
|
+
}
|
|
40
|
+
)
|
|
42
41
|
)
|
|
43
42
|
trace_api.set_tracer_provider(tracer_provider)
|
|
44
43
|
|
|
@@ -48,7 +47,9 @@ def init_tracing():
|
|
|
48
47
|
try:
|
|
49
48
|
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
|
50
49
|
|
|
51
|
-
RequestsInstrumentor().instrument(
|
|
50
|
+
RequestsInstrumentor().instrument(
|
|
51
|
+
tracer_provider=tracer_provider,
|
|
52
|
+
)
|
|
52
53
|
except ImportError:
|
|
53
54
|
# Its possible that this environment has opentelemetry but not requests
|
|
54
55
|
# instrumentation. We don't want to fail in that case.
|
|
@@ -58,8 +59,10 @@ def init_tracing():
|
|
|
58
59
|
@contextlib.contextmanager
|
|
59
60
|
def post_fork():
|
|
60
61
|
global tracer_provider
|
|
62
|
+
|
|
61
63
|
tracer_provider = None
|
|
62
64
|
init_tracing()
|
|
65
|
+
|
|
63
66
|
token = context.attach(extract(os.environ))
|
|
64
67
|
try:
|
|
65
68
|
tracer = trace_api.get_tracer_provider().get_tracer(__name__)
|
|
@@ -67,47 +70,27 @@ def post_fork():
|
|
|
67
70
|
"fork", kind=trace_api.SpanKind.SERVER
|
|
68
71
|
) as span:
|
|
69
72
|
span.set_attribute("cmd", " ".join(sys.argv))
|
|
73
|
+
span.set_attribute("pid", str(os.getpid()))
|
|
70
74
|
yield
|
|
71
75
|
finally:
|
|
72
76
|
context.detach(token)
|
|
73
77
|
|
|
74
78
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
if i > 0 and tokens[i - 1] == before_token:
|
|
78
|
-
return tok
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def cli_entrypoint(name: str):
|
|
82
|
-
def cli_entrypoint_wrap(func):
|
|
79
|
+
def cli(name: str):
|
|
80
|
+
def cli_wrap(func):
|
|
83
81
|
@wraps(func)
|
|
84
82
|
def wrapper_func(*args, **kwargs):
|
|
85
83
|
global tracer_provider
|
|
86
|
-
|
|
87
84
|
init_tracing()
|
|
88
85
|
|
|
89
|
-
|
|
86
|
+
if tracer_provider is None:
|
|
87
|
+
return func(*args, **kwargs)
|
|
90
88
|
|
|
91
89
|
token = context.attach(extract(os.environ))
|
|
92
90
|
try:
|
|
93
91
|
tracer = trace_api.get_tracer_provider().get_tracer(__name__)
|
|
94
|
-
|
|
95
|
-
card_subcommand = _extract_token_after(sys.argv, "card")
|
|
96
|
-
|
|
97
|
-
step_name = _extract_token_after(sys.argv, "step")
|
|
98
|
-
task_id = _extract_token_after(sys.argv, "--task-id")
|
|
99
|
-
run_id = _extract_token_after(sys.argv, "--run-id")
|
|
100
|
-
if step_name and task_id and run_id:
|
|
101
|
-
better_name = "/".join([run_id, step_name, task_id])
|
|
102
|
-
elif card_subcommand:
|
|
103
|
-
better_name = "card/" + card_subcommand
|
|
104
|
-
elif "run" in sys.argv:
|
|
105
|
-
better_name = "run"
|
|
106
|
-
else:
|
|
107
|
-
better_name = None
|
|
108
|
-
|
|
109
92
|
with tracer.start_as_current_span(
|
|
110
|
-
|
|
93
|
+
name, kind=trace_api.SpanKind.SERVER
|
|
111
94
|
) as span:
|
|
112
95
|
span.set_attribute("cmd", " ".join(sys.argv))
|
|
113
96
|
span.set_attribute("pid", str(os.getpid()))
|
|
@@ -121,7 +104,7 @@ def cli_entrypoint(name: str):
|
|
|
121
104
|
|
|
122
105
|
return wrapper_func
|
|
123
106
|
|
|
124
|
-
return
|
|
107
|
+
return cli_wrap
|
|
125
108
|
|
|
126
109
|
|
|
127
110
|
def inject_tracing_vars(env_dict: Dict[str, str]) -> Dict[str, str]:
|
|
@@ -130,23 +113,32 @@ def inject_tracing_vars(env_dict: Dict[str, str]) -> Dict[str, str]:
|
|
|
130
113
|
|
|
131
114
|
|
|
132
115
|
def get_trace_id() -> str:
|
|
133
|
-
|
|
116
|
+
try:
|
|
117
|
+
return format_trace_id(trace_api.get_current_span().get_span_context().trace_id)
|
|
118
|
+
except Exception:
|
|
119
|
+
return ""
|
|
134
120
|
|
|
135
121
|
|
|
136
122
|
@contextlib.contextmanager
|
|
137
|
-
def traced(name, attrs=
|
|
123
|
+
def traced(name: str, attrs: Optional[Dict] = None):
|
|
124
|
+
if tracer_provider is None:
|
|
125
|
+
yield
|
|
126
|
+
return
|
|
138
127
|
tracer = trace_api.get_tracer_provider().get_tracer(__name__)
|
|
139
128
|
with tracer.start_as_current_span(name) as span:
|
|
140
|
-
|
|
141
|
-
|
|
129
|
+
if attrs:
|
|
130
|
+
for k, v in attrs.items():
|
|
131
|
+
span.set_attribute(k, v)
|
|
142
132
|
yield
|
|
143
133
|
|
|
144
134
|
|
|
145
135
|
def tracing(func):
|
|
146
136
|
@wraps(func)
|
|
147
137
|
def wrapper_func(*args, **kwargs):
|
|
148
|
-
|
|
138
|
+
if tracer_provider is None:
|
|
139
|
+
return func(*args, **kwargs)
|
|
149
140
|
|
|
141
|
+
tracer = trace_api.get_tracer_provider().get_tracer(func.__module__)
|
|
150
142
|
with tracer.start_as_current_span(func.__name__):
|
|
151
143
|
return func(*args, **kwargs)
|
|
152
144
|
|
metaflow/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
metaflow_version = "2.14.
|
|
1
|
+
metaflow_version = "2.14.2.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ob-metaflow
|
|
3
|
-
Version: 2.14.
|
|
3
|
+
Version: 2.14.2.1
|
|
4
4
|
Summary: Metaflow: More Data Science, Less Engineering
|
|
5
5
|
Author: Netflix, Outerbounds & the Metaflow Community
|
|
6
6
|
Author-email: help@outerbounds.co
|
|
@@ -12,7 +12,7 @@ Requires-Dist: boto3
|
|
|
12
12
|
Requires-Dist: pylint
|
|
13
13
|
Requires-Dist: kubernetes
|
|
14
14
|
Provides-Extra: stubs
|
|
15
|
-
Requires-Dist: metaflow-stubs==2.14.
|
|
15
|
+
Requires-Dist: metaflow-stubs==2.14.2.1; extra == "stubs"
|
|
16
16
|
Dynamic: author
|
|
17
17
|
Dynamic: author-email
|
|
18
18
|
Dynamic: description
|