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.

Files changed (31) hide show
  1. metaflow/cli.py +0 -23
  2. metaflow/cli_components/run_cmds.py +34 -14
  3. metaflow/cli_components/step_cmd.py +2 -0
  4. metaflow/client/core.py +241 -1
  5. metaflow/cmd/main_cli.py +1 -1
  6. metaflow/metadata_provider/heartbeat.py +1 -0
  7. metaflow/metadata_provider/metadata.py +33 -0
  8. metaflow/metaflow_config.py +5 -9
  9. metaflow/mflog/save_logs.py +2 -2
  10. metaflow/plugins/argo/argo_workflows.py +12 -8
  11. metaflow/plugins/argo/argo_workflows_cli.py +2 -2
  12. metaflow/plugins/datatools/s3/s3op.py +4 -4
  13. metaflow/plugins/env_escape/server.py +7 -0
  14. metaflow/plugins/env_escape/stub.py +21 -4
  15. metaflow/plugins/kubernetes/kubernetes_cli.py +1 -1
  16. metaflow/plugins/metadata_providers/local.py +66 -0
  17. metaflow/plugins/metadata_providers/service.py +51 -0
  18. metaflow/plugins/pypi/bootstrap.py +4 -4
  19. metaflow/runner/click_api.py +6 -3
  20. metaflow/sidecar/sidecar_worker.py +1 -1
  21. metaflow/task.py +21 -2
  22. metaflow/tracing/__init__.py +7 -7
  23. metaflow/tracing/span_exporter.py +31 -38
  24. metaflow/tracing/tracing_modules.py +35 -43
  25. metaflow/version.py +1 -1
  26. {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/METADATA +2 -2
  27. {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/RECORD +31 -31
  28. {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/LICENSE +0 -0
  29. {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/WHEEL +0 -0
  30. {ob_metaflow-2.14.0.1.dist-info → ob_metaflow-2.14.2.1.dist-info}/entry_points.txt +0 -0
  31. {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
- return cls.___class_connection___.stub_request(
280
- None, OP_INIT, cls.___class_remote_class_name___, *args, **kwargs
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 method_type == NORMAL_METHOD:
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.cli_entrypoint("kubernetes/step")
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, CONDA_USE_FAST_INIT
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 CONDA_USE_FAST_INIT:
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(
@@ -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 isinstance(v, list):
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.cli_entrypoint("sidecar")
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
 
@@ -20,15 +20,15 @@ def post_fork():
20
20
  yield
21
21
 
22
22
 
23
- def cli_entrypoint(name: str):
24
- def cli_entrypoint_wrap(func):
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 cli_entrypoint_wrap
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
- cli_entrypoint,
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
- if OTEL_ENDPOINT:
15
- return set_otel_exporter()
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
- elif ZIPKIN_ENDPOINT:
18
- return set_zipkin_exporter()
22
+ for config, create_exporter in exporter_map.items():
23
+ if config:
24
+ return create_exporter()
19
25
 
20
- elif CONSOLE_TRACE_ENABLED:
21
- return set_console_exporter()
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 set_otel_exporter():
30
+ def _create_otel_exporter():
28
31
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
29
32
 
30
- from metaflow.metaflow_config import (
31
- SERVICE_AUTH_KEY,
32
- SERVICE_HEADERS,
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
- span_exporter = OTLPSpanExporter(
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
- span_exporter = OTLPSpanExporter(
43
- endpoint=OTEL_ENDPOINT,
44
- headers=SERVICE_HEADERS,
45
- timeout=15,
46
- )
47
- else:
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
- span_exporter = ZipkinExporter(
57
- endpoint=ZIPKIN_ENDPOINT,
58
- )
59
- return span_exporter
53
+ return ZipkinExporter(endpoint=ZIPKIN_ENDPOINT)
60
54
 
61
55
 
62
- def set_console_exporter():
56
+ def _create_console_exporter():
63
57
  from opentelemetry.sdk.trace.export import ConsoleSpanExporter
64
58
 
65
- span_exporter = ConsoleSpanExporter()
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
- if "METAFLOW_KUBERNETES_POD_NAMESPACE" in os.environ:
34
- service_name = "metaflow-kubernetes"
35
- elif "AWS_BATCH_JOB_ID" in os.environ:
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({SERVICE_NAME: service_name})
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 _extract_token_after(tokens: List[str], before_token: str) -> Optional[str]:
76
- for i, tok in enumerate(tokens):
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
- assert tracer_provider is not None # make type checker happy
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
- better_name or name, kind=trace_api.SpanKind.SERVER
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 cli_entrypoint_wrap
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
- return format_trace_id(trace_api.get_current_span().get_span_context().trace_id)
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
- for k, v in attrs.items():
141
- span.set_attribute(k, v)
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
- tracer = trace_api.get_tracer_provider().get_tracer(func.__module__.__name__)
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.0.1"
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.0.1
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.0.1; extra == "stubs"
15
+ Requires-Dist: metaflow-stubs==2.14.2.1; extra == "stubs"
16
16
  Dynamic: author
17
17
  Dynamic: author-email
18
18
  Dynamic: description