metaflow 2.8.1__py2.py3-none-any.whl → 2.8.3__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.
Files changed (54) hide show
  1. metaflow/client/core.py +14 -4
  2. metaflow/cmd/configure_cmd.py +3 -3
  3. metaflow/cmd/main_cli.py +9 -14
  4. metaflow/current.py +15 -0
  5. metaflow/datastore/datastore_set.py +7 -7
  6. metaflow/datastore/flow_datastore.py +1 -2
  7. metaflow/extension_support/__init__.py +1 -0
  8. metaflow/extension_support/integrations.py +141 -0
  9. metaflow/integrations.py +29 -0
  10. metaflow/metaflow_config.py +21 -0
  11. metaflow/metaflow_environment.py +5 -4
  12. metaflow/package.py +1 -1
  13. metaflow/plugins/airflow/airflow.py +0 -1
  14. metaflow/plugins/argo/argo_workflows.py +2 -0
  15. metaflow/plugins/argo/argo_workflows_cli.py +11 -1
  16. metaflow/plugins/aws/aws_utils.py +6 -1
  17. metaflow/plugins/aws/batch/batch.py +30 -8
  18. metaflow/plugins/aws/batch/batch_cli.py +12 -0
  19. metaflow/plugins/aws/batch/batch_client.py +39 -2
  20. metaflow/plugins/aws/batch/batch_decorator.py +23 -0
  21. metaflow/plugins/aws/step_functions/step_functions.py +7 -4
  22. metaflow/plugins/aws/step_functions/step_functions_cli.py +11 -1
  23. metaflow/plugins/cards/card_modules/bundle.css +56 -56
  24. metaflow/plugins/cards/card_modules/convert_to_native_type.py +67 -5
  25. metaflow/plugins/cards/card_modules/main.js +14 -7
  26. metaflow/plugins/conda/conda_environment.py +2 -2
  27. metaflow/plugins/conda/conda_step_decorator.py +7 -1
  28. metaflow/plugins/datatools/s3/s3.py +2 -2
  29. metaflow/plugins/env_escape/communication/channel.py +1 -1
  30. metaflow/plugins/kubernetes/kubernetes.py +4 -0
  31. metaflow/plugins/kubernetes/kubernetes_decorator.py +6 -2
  32. metaflow/plugins/kubernetes/kubernetes_job.py +17 -2
  33. metaflow/plugins/metadata/service.py +3 -2
  34. metaflow/runtime.py +5 -3
  35. metaflow/tutorials/02-statistics/README.md +4 -9
  36. metaflow/tutorials/02-statistics/stats.py +38 -11
  37. metaflow/tutorials/03-playlist-redux/playlist.py +24 -16
  38. metaflow/tutorials/04-playlist-plus/playlist.py +14 -23
  39. metaflow/tutorials/05-hello-cloud/README.md +45 -0
  40. metaflow/tutorials/{05-helloaws/helloaws.ipynb → 05-hello-cloud/hello-cloud.ipynb} +10 -5
  41. metaflow/tutorials/{05-helloaws/helloaws.py → 05-hello-cloud/hello-cloud.py} +11 -13
  42. metaflow/tutorials/06-statistics-redux/README.md +6 -29
  43. metaflow/tutorials/06-statistics-redux/stats.ipynb +2 -2
  44. metaflow/tutorials/07-worldview/README.md +3 -11
  45. metaflow/tutorials/07-worldview/worldview.ipynb +3 -3
  46. metaflow/tutorials/08-autopilot/README.md +10 -17
  47. metaflow/tutorials/08-autopilot/autopilot.ipynb +12 -7
  48. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/METADATA +1 -6
  49. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/RECORD +53 -51
  50. metaflow/tutorials/05-helloaws/README.md +0 -27
  51. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/LICENSE +0 -0
  52. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/WHEEL +0 -0
  53. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/entry_points.txt +0 -0
  54. {metaflow-2.8.1.dist-info → metaflow-2.8.3.dist-info}/top_level.txt +0 -0
@@ -149,7 +149,12 @@ class BatchJob(object):
149
149
  max_swap,
150
150
  swappiness,
151
151
  inferentia,
152
+ memory,
152
153
  host_volumes,
154
+ use_tmpfs,
155
+ tmpfs_tempdir,
156
+ tmpfs_size,
157
+ tmpfs_path,
153
158
  num_parallel,
154
159
  ):
155
160
  # identify platform from any compute environment associated with the
@@ -251,7 +256,7 @@ class BatchJob(object):
251
256
  if inferentia:
252
257
  if not (isinstance(inferentia, (int, unicode, basestring))):
253
258
  raise BatchJobException(
254
- "invalid inferentia value: ({}) (should be 0 or greater)".format(
259
+ "Invalid inferentia value: ({}) (should be 0 or greater)".format(
255
260
  inferentia
256
261
  )
257
262
  )
@@ -282,6 +287,29 @@ class BatchJob(object):
282
287
  {"sourceVolume": name, "containerPath": host_path}
283
288
  )
284
289
 
290
+ if use_tmpfs or (tmpfs_size and use_tmpfs is None):
291
+ if tmpfs_size:
292
+ if not (isinstance(tmpfs_size, (int, unicode, basestring))):
293
+ raise BatchJobException(
294
+ "Invalid tmpfs value: ({}) (should be 0 or greater)".format(
295
+ tmpfs_size
296
+ )
297
+ )
298
+ else:
299
+ # default tmpfs behavior - https://man7.org/linux/man-pages/man5/tmpfs.5.html
300
+ tmpfs_size = int(memory) / 2
301
+
302
+ job_definition["containerProperties"]["linuxParameters"]["tmpfs"] = [
303
+ {
304
+ "containerPath": tmpfs_path,
305
+ "size": int(tmpfs_size),
306
+ "mountOptions": [
307
+ # should map to rw, suid, dev, exec, auto, nouser, and async
308
+ "defaults"
309
+ ],
310
+ }
311
+ ]
312
+
285
313
  self.num_parallel = num_parallel or 0
286
314
  if self.num_parallel >= 1:
287
315
  job_definition["type"] = "multinode"
@@ -343,7 +371,12 @@ class BatchJob(object):
343
371
  max_swap,
344
372
  swappiness,
345
373
  inferentia,
374
+ memory,
346
375
  host_volumes,
376
+ use_tmpfs,
377
+ tmpfs_tempdir,
378
+ tmpfs_size,
379
+ tmpfs_path,
347
380
  num_parallel,
348
381
  ):
349
382
  self.payload["jobDefinition"] = self._register_job_definition(
@@ -355,7 +388,12 @@ class BatchJob(object):
355
388
  max_swap,
356
389
  swappiness,
357
390
  inferentia,
391
+ memory,
358
392
  host_volumes,
393
+ use_tmpfs,
394
+ tmpfs_tempdir,
395
+ tmpfs_size,
396
+ tmpfs_path,
359
397
  num_parallel,
360
398
  )
361
399
  return self
@@ -522,7 +560,6 @@ class TriableException(Exception):
522
560
 
523
561
 
524
562
  class RunningJob(object):
525
-
526
563
  NUM_RETRIES = 8
527
564
 
528
565
  def __init__(self, id, client):
@@ -71,6 +71,16 @@ class BatchDecorator(StepDecorator):
71
71
  A swappiness value of 0 causes swapping not to happen unless absolutely
72
72
  necessary. A swappiness value of 100 causes pages to be swapped very
73
73
  aggressively. Accepted values are whole numbers between 0 and 100.
74
+ use_tmpfs: bool, default: False
75
+ This enables an explicit tmpfs mount for this step.
76
+ tmpfs_tempdir: bool, default: True
77
+ sets METAFLOW_TEMPDIR to tmpfs_path if set for this step.
78
+ tmpfs_size: int, optional
79
+ The value for the size (in MiB) of the tmpfs mount for this step.
80
+ This parameter maps to the `--tmpfs` option in Docker. Defaults to 50% of the
81
+ memory allocated for this step.
82
+ tmpfs_path: string, optional
83
+ Path to tmpfs mount for this step. Defaults to /metaflow_temp.
74
84
  inferentia : int, default: 0
75
85
  Number of Inferentia chips required for this step.
76
86
  """
@@ -89,6 +99,10 @@ class BatchDecorator(StepDecorator):
89
99
  "swappiness": None,
90
100
  "inferentia": None,
91
101
  "host_volumes": None,
102
+ "use_tmpfs": False,
103
+ "tmpfs_tempdir": True,
104
+ "tmpfs_size": None,
105
+ "tmpfs_path": "/metaflow_temp",
92
106
  }
93
107
  resource_defaults = {
94
108
  "cpu": "1",
@@ -153,6 +167,10 @@ class BatchDecorator(StepDecorator):
153
167
  "least 60 seconds for execution on AWS Batch.".format(step=step)
154
168
  )
155
169
 
170
+ # Validate tmpfs_path. Batch requires this to be an absolute path
171
+ if self.attributes["tmpfs_path"] and self.attributes["tmpfs_path"][0] != "/":
172
+ raise BatchException("'tmpfs_path' needs to be an absolute path")
173
+
156
174
  def runtime_init(self, flow, graph, package, run_id):
157
175
  # Set some more internal state.
158
176
  self.flow = flow
@@ -198,6 +216,11 @@ class BatchDecorator(StepDecorator):
198
216
  self.metadata = metadata
199
217
  self.task_datastore = task_datastore
200
218
 
219
+ # current.tempdir reflects the value of METAFLOW_TEMPDIR (the current working
220
+ # directory by default), or the value of tmpfs_path if tmpfs_tempdir=False.
221
+ if not self.attributes["tmpfs_tempdir"]:
222
+ current._update_env({"tempdir": self.attributes["tmpfs_path"]})
223
+
201
224
  # task_pre_step may run locally if fallback is activated for @catch
202
225
  # decorator. In that scenario, we skip collecting AWS Batch execution
203
226
  # metadata. A rudimentary way to detect non-local execution is to
@@ -227,7 +227,6 @@ class StepFunctions(object):
227
227
  return None
228
228
 
229
229
  def _compile(self):
230
-
231
230
  # Visit every node of the flow and recursively build the state machine.
232
231
  def _visit(node, workflow, exit_node=None):
233
232
  if node.parallel_foreach:
@@ -637,8 +636,7 @@ class StepFunctions(object):
637
636
  env["METAFLOW_SFN_DYNAMO_DB_TABLE"] = SFN_DYNAMO_DB_TABLE
638
637
 
639
638
  # It makes no sense to set env vars to None (shows up as "None" string)
640
- env_without_none_values = {k: v for k, v in env.items() if v is not None}
641
- del env
639
+ env = {k: v for k, v in env.items() if v is not None}
642
640
 
643
641
  # Resolve AWS Batch resource requirements.
644
642
  batch_deco = [deco for deco in node.decorators if deco.name == "batch"][0]
@@ -686,7 +684,12 @@ class StepFunctions(object):
686
684
  shared_memory=resources["shared_memory"],
687
685
  max_swap=resources["max_swap"],
688
686
  swappiness=resources["swappiness"],
689
- env=env_without_none_values,
687
+ use_tmpfs=resources["use_tmpfs"],
688
+ tmpfs_tempdir=resources["tmpfs_tempdir"],
689
+ tmpfs_size=resources["tmpfs_size"],
690
+ tmpfs_path=resources["tmpfs_path"],
691
+ inferentia=resources["inferentia"],
692
+ env=env,
690
693
  attrs=attrs,
691
694
  host_volumes=resources["host_volumes"],
692
695
  )
@@ -9,6 +9,7 @@ from metaflow import current, decorators, parameters, JSONType
9
9
  from metaflow.metaflow_config import (
10
10
  SERVICE_VERSION_CHECK,
11
11
  SFN_STATE_MACHINE_PREFIX,
12
+ UI_URL,
12
13
  )
13
14
  from metaflow.exception import MetaflowException, MetaflowInternalError
14
15
  from metaflow.package import MetaflowPackage
@@ -311,7 +312,6 @@ def make_flow(
311
312
  def resolve_token(
312
313
  name, token_prefix, obj, authorize, given_token, generate_new_token, is_project
313
314
  ):
314
-
315
315
  # 1) retrieve the previous deployment, if one exists
316
316
  workflow = StepFunctions.get_existing_deployment(name)
317
317
  if workflow is None:
@@ -452,6 +452,16 @@ def trigger(obj, run_id_file=None, **kwargs):
452
452
  bold=True,
453
453
  )
454
454
 
455
+ run_url = (
456
+ "%s/%s/%s" % (UI_URL.rstrip("/"), obj.flow.name, run_id) if UI_URL else None
457
+ )
458
+
459
+ if run_url:
460
+ obj.echo(
461
+ "See the run in the UI at %s" % run_url,
462
+ bold=True,
463
+ )
464
+
455
465
 
456
466
  @step_functions.command(help="List all runs of the workflow on AWS Step Functions.")
457
467
  @click.option(
@@ -1,57 +1,4 @@
1
- .container.svelte-teyund{width:100%;display:flex;flex-direction:column;position:relative}/* PrismJS 1.25.0
2
- https://prismjs.com/download.html#themes=prism&languages=clike+python */
3
- code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
4
- @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap");
5
-
6
- :root {
7
- --bg: #ffffff;
8
- --black: #333;
9
- --blue: #0c66de;
10
- --dk-grey: #767676;
11
- --dk-primary: #ef863b;
12
- --dk-secondary: #13172d;
13
- --dk-tertiary: #0f426e;
14
- --error: #cf483e;
15
- --grey: rgba(0, 0, 0, 0.125);
16
- --highlight: #f8d9d8;
17
- --lt-blue: #4fa7ff;
18
- --lt-grey: #f3f3f3;
19
- --lt-lt-grey: #f9f9f9;
20
- --lt-primary: #ffcb8b;
21
- --lt-secondary: #434d81;
22
- --lt-tertiary: #4189c9;
23
- --primary: #faab4a;
24
- --quadrary: #f8d9d8;
25
- --secondary: #2e3454;
26
- --tertiary: #2a679d;
27
- --white: #ffffff;
28
-
29
- --component-spacer: 3rem;
30
- --aside-width: 20rem;
31
- --embed-card-min-height: 12rem;
32
-
33
- --mono-font: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
34
- "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
35
- "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
36
- }
37
-
38
- html, body {
39
- margin: 0;
40
- min-height: 100vh;
41
- overflow-y: visible;
42
- padding: 0;
43
- width: 100%;
44
- }
45
-
46
- .card_app {
47
- width: 100%;
48
- min-height: 100vh;
49
- }
50
-
51
- .embed .card_app {
52
- min-height: var(--embed-card-min-height);
53
- }
54
- .mf-card * {
1
+ .container.svelte-teyund{width:100%;display:flex;flex-direction:column;position:relative}.mf-card * {
55
2
  box-sizing: border-box;
56
3
  }
57
4
 
@@ -155,7 +102,60 @@ html, body {
155
102
  text-shadow: none;
156
103
  user-select: auto;
157
104
  }
158
- aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media(min-width: 60rem){aside.svelte-1okdv0e{display:flex;flex-direction:column;height:100vh;justify-content:space-between;padding:2.5rem 0 1.5rem 1.5rem;position:fixed;width:var(--aside-width)}}.embed aside{display:none}aside ul{list-style-type:none}aside a, aside button, aside a:visited{text-decoration:none;cursor:pointer;font-weight:700;color:var(--black)}aside a:hover, aside button:hover{text-decoration:underline}.logoContainer svg{width:100%;max-width:140px;margin-bottom:3.75rem;height:auto}.modal.svelte-1hhf5ym{align-items:center;background:rgba(0, 0, 0, 0.5);bottom:0;cursor:pointer;display:flex;height:100%;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;width:100%;z-index:100}.modalContainer > *{background-color:white;border-radius:5px;cursor:default;flex:0 1 auto;padding:1rem;position:relative}.modal img{max-height:80vh !important}.cancelButton.svelte-1hhf5ym{color:white;cursor:pointer;font-size:2rem;position:absolute;right:1rem;top:1rem}.cancelButton.svelte-1hhf5ym:hover{color:var(--blue)}.nav.svelte-1kdpgko.svelte-1kdpgko{border-radius:0 0 5px 0;display:none;margin:0;top:0}ul.navList.svelte-1kdpgko.svelte-1kdpgko{list-style-type:none}ul.navList.svelte-1kdpgko ul.svelte-1kdpgko{margin:0.5rem 1rem 2rem}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0}.navItem.svelte-1kdpgko li.svelte-1kdpgko:hover{color:var(--blue)}.pageId.svelte-1kdpgko.svelte-1kdpgko{display:block;border-bottom:1px solid var(--grey);padding:0 0.5rem;margin-bottom:1rem}@media(min-width: 60rem){.nav.svelte-1kdpgko.svelte-1kdpgko{display:block}ul.navList.svelte-1kdpgko.svelte-1kdpgko{text-align:left}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0.5rem 0}}.mainContainer.svelte-13ho8jo{max-width:110rem}main.svelte-13ho8jo{flex:0 1 auto;max-width:100rem;padding:1.5rem}@media(min-width: 60rem){main.svelte-13ho8jo{margin-left:var(--aside-width)}}.embed main{margin:0 auto}.container.svelte-ubs992{width:100%;overflow:auto}table.svelte-ubs992{width:100%}figure.svelte-1x96yvr{background:var(--lt-grey);padding:1rem;border-radius:5px;text-align:center;margin:0 auto var(--component-spacer)}@media(min-width: 60rem){figure.svelte-1x96yvr{margin-bottom:0}}img.svelte-1x96yvr{max-width:100%;max-height:500px}.label.svelte-1x96yvr{font-weight:bold;margin:0.5rem 0}.description.svelte-1x96yvr{font-size:0.9rem;font-style:italic;text-align:center;margin:0.5rem 0}header.svelte-1ugmt5d{margin-bottom:var(--component-spacer)}.log.svelte-1jhmsu{background:var(--lt-grey) !important;font-size:0.9rem;padding:2rem}.heading.svelte-17n0qr8{margin-bottom:1.5rem}.sectionItems.svelte-17n0qr8{display:block}.sectionItems .imageContainer{max-height:500px}.container.svelte-17n0qr8{scroll-margin:var(--component-spacer)}hr.svelte-17n0qr8{background:var(--grey);border:none;height:1px;margin:var(--component-spacer) 0;padding:0}@media(min-width: 60rem){.sectionItems.svelte-17n0qr8{display:grid;grid-gap:2rem}}.subtitle.svelte-lu9pnn{font-size:1rem;text-align:left}.page.svelte-v7ihqd:last-of-type{margin-bottom:var(--component-spacer)}.page:last-of-type section:last-of-type hr{display:none}.title.svelte-117s0ws{text-align:left}.idCell.svelte-pt8vzv{font-weight:bold;text-align:right;background:var(--lt-grey);width:12%}.codeCell.svelte-pt8vzv{text-align:left;user-select:all}td.svelte-gl9h79{text-align:left}td.labelColumn.svelte-gl9h79{text-align:right;background-color:var(--lt-grey);font-weight:700;width:12%;white-space:nowrap}.tableContainer.svelte-q3hq57{overflow:auto}th.svelte-q3hq57{position:sticky;top:-1px;z-index:2;white-space:nowrap;background:var(--white)}:root {
105
+ /* PrismJS 1.25.0
106
+ https://prismjs.com/download.html#themes=prism&languages=clike+python */
107
+ code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
108
+ @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap");
109
+
110
+ :root {
111
+ --bg: #ffffff;
112
+ --black: #333;
113
+ --blue: #0c66de;
114
+ --dk-grey: #767676;
115
+ --dk-primary: #ef863b;
116
+ --dk-secondary: #13172d;
117
+ --dk-tertiary: #0f426e;
118
+ --error: #cf483e;
119
+ --grey: rgba(0, 0, 0, 0.125);
120
+ --highlight: #f8d9d8;
121
+ --lt-blue: #4fa7ff;
122
+ --lt-grey: #f3f3f3;
123
+ --lt-lt-grey: #f9f9f9;
124
+ --lt-primary: #ffcb8b;
125
+ --lt-secondary: #434d81;
126
+ --lt-tertiary: #4189c9;
127
+ --primary: #faab4a;
128
+ --quadrary: #f8d9d8;
129
+ --secondary: #2e3454;
130
+ --tertiary: #2a679d;
131
+ --white: #ffffff;
132
+
133
+ --component-spacer: 3rem;
134
+ --aside-width: 20rem;
135
+ --embed-card-min-height: 12rem;
136
+
137
+ --mono-font: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
138
+ "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
139
+ "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
140
+ }
141
+
142
+ html, body {
143
+ margin: 0;
144
+ min-height: 100vh;
145
+ overflow-y: visible;
146
+ padding: 0;
147
+ width: 100%;
148
+ }
149
+
150
+ .card_app {
151
+ width: 100%;
152
+ min-height: 100vh;
153
+ }
154
+
155
+ .embed .card_app {
156
+ min-height: var(--embed-card-min-height);
157
+ }
158
+ aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media(min-width: 60rem){aside.svelte-1okdv0e{display:flex;flex-direction:column;height:100vh;justify-content:space-between;padding:2.5rem 0 1.5rem 1.5rem;position:fixed;width:var(--aside-width)}}.embed aside{display:none}aside ul{list-style-type:none}aside a, aside button, aside a:visited{text-decoration:none;cursor:pointer;font-weight:700;color:var(--black)}aside a:hover, aside button:hover{text-decoration:underline}.logoContainer svg{width:100%;max-width:140px;margin-bottom:3.75rem;height:auto}.mainContainer.svelte-13ho8jo{max-width:110rem}main.svelte-13ho8jo{flex:0 1 auto;max-width:100rem;padding:1.5rem}@media(min-width: 60rem){main.svelte-13ho8jo{margin-left:var(--aside-width)}}.embed main{margin:0 auto}.modal.svelte-1hhf5ym{align-items:center;background:rgba(0, 0, 0, 0.5);bottom:0;cursor:pointer;display:flex;height:100%;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;width:100%;z-index:100}.modalContainer > *{background-color:white;border-radius:5px;cursor:default;flex:0 1 auto;padding:1rem;position:relative}.modal img{max-height:80vh !important}.cancelButton.svelte-1hhf5ym{color:white;cursor:pointer;font-size:2rem;position:absolute;right:1rem;top:1rem}.cancelButton.svelte-1hhf5ym:hover{color:var(--blue)}.nav.svelte-1kdpgko.svelte-1kdpgko{border-radius:0 0 5px 0;display:none;margin:0;top:0}ul.navList.svelte-1kdpgko.svelte-1kdpgko{list-style-type:none}ul.navList.svelte-1kdpgko ul.svelte-1kdpgko{margin:0.5rem 1rem 2rem}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0}.navItem.svelte-1kdpgko li.svelte-1kdpgko:hover{color:var(--blue)}.pageId.svelte-1kdpgko.svelte-1kdpgko{display:block;border-bottom:1px solid var(--grey);padding:0 0.5rem;margin-bottom:1rem}@media(min-width: 60rem){.nav.svelte-1kdpgko.svelte-1kdpgko{display:block}ul.navList.svelte-1kdpgko.svelte-1kdpgko{text-align:left}.navList.svelte-1kdpgko li.svelte-1kdpgko{display:block;margin:0.5rem 0}}.page.svelte-v7ihqd:last-of-type{margin-bottom:var(--component-spacer)}.page:last-of-type section:last-of-type hr{display:none}.container.svelte-ubs992{width:100%;overflow:auto}table.svelte-ubs992{width:100%}.heading.svelte-17n0qr8{margin-bottom:1.5rem}.sectionItems.svelte-17n0qr8{display:block}.sectionItems .imageContainer{max-height:500px}.container.svelte-17n0qr8{scroll-margin:var(--component-spacer)}hr.svelte-17n0qr8{background:var(--grey);border:none;height:1px;margin:var(--component-spacer) 0;padding:0}@media(min-width: 60rem){.sectionItems.svelte-17n0qr8{display:grid;grid-gap:2rem}}header.svelte-1ugmt5d{margin-bottom:var(--component-spacer)}.subtitle.svelte-lu9pnn{font-size:1rem;text-align:left}.log.svelte-1jhmsu{background:var(--lt-grey) !important;font-size:0.9rem;padding:2rem}.title.svelte-117s0ws{text-align:left}figure.svelte-1x96yvr{background:var(--lt-grey);padding:1rem;border-radius:5px;text-align:center;margin:0 auto var(--component-spacer)}@media(min-width: 60rem){figure.svelte-1x96yvr{margin-bottom:0}}img.svelte-1x96yvr{max-width:100%;max-height:500px}.label.svelte-1x96yvr{font-weight:bold;margin:0.5rem 0}.description.svelte-1x96yvr{font-size:0.9rem;font-style:italic;text-align:center;margin:0.5rem 0}.idCell.svelte-pt8vzv{font-weight:bold;text-align:right;background:var(--lt-grey);width:12%}.codeCell.svelte-pt8vzv{text-align:left;user-select:all}td.svelte-gl9h79{text-align:left}td.labelColumn.svelte-gl9h79{text-align:right;background-color:var(--lt-grey);font-weight:700;width:12%;white-space:nowrap}.tableContainer.svelte-q3hq57{overflow:auto}th.svelte-q3hq57{position:sticky;top:-1px;z-index:2;white-space:nowrap;background:var(--white)}.stepwrapper.svelte-18aex7a{display:flex;align-items:center;flex-direction:column;width:100%;position:relative;min-width:var(--dag-step-width)}.childwrapper.svelte-18aex7a{display:flex;width:100%}.gap.svelte-18aex7a{height:var(--dag-gap)}:root {
159
159
  --dag-border: #282828;
160
160
  --dag-bg-static: var(--lt-grey);
161
161
  --dag-bg-success: #a5d46a;
@@ -167,4 +167,4 @@ aside.svelte-1okdv0e{display:none;line-height:2;text-align:left}@media(min-width
167
167
  --dag-step-width: 11.25rem;
168
168
  --dag-selected: #ffd700;
169
169
  }
170
- .stepwrapper.svelte-18aex7a{display:flex;align-items:center;flex-direction:column;width:100%;position:relative;min-width:var(--dag-step-width)}.childwrapper.svelte-18aex7a{display:flex;width:100%}.gap.svelte-18aex7a{height:var(--dag-gap)}.wrapper.svelte-117ceti.svelte-117ceti{position:relative;z-index:1}.step.svelte-117ceti.svelte-117ceti{font-size:0.75rem;padding:0.5rem;color:var(--dk-grey)}.rectangle.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-static);border:1px solid var(--dag-border);box-sizing:border-box;position:relative;height:var(--dag-step-height);width:var(--dag-step-width)}.rectangle.error.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-error)}.rectangle.success.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-success)}.rectangle.running.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-running)}.level.svelte-117ceti.svelte-117ceti{z-index:-1;filter:contrast(0.5);position:absolute}.inner.svelte-117ceti.svelte-117ceti{position:relative;height:100%;width:100%}.name.svelte-117ceti.svelte-117ceti{font-weight:bold;overflow:hidden;text-overflow:ellipsis;display:block}.description.svelte-117ceti.svelte-117ceti{position:absolute;max-height:4rem;bottom:0;left:0;right:0;overflow:hidden;-webkit-line-clamp:4;line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical}.overflown.description.svelte-117ceti.svelte-117ceti{cursor:help}.current.svelte-117ceti .rectangle.svelte-117ceti{box-shadow:0 0 10px var(--dag-selected)}.levelstoshow.svelte-117ceti.svelte-117ceti{position:absolute;bottom:100%;right:0;font-size:0.75rem;font-weight:100;text-align:right}.connectorwrapper.svelte-1hyaq5f{transform-origin:0 0;position:absolute;z-index:0;min-width:var(--strokeWidth)}.flip.svelte-1hyaq5f{transform:scaleX(-1)}.path.svelte-1hyaq5f{--strokeWidth:0.5rem;--strokeColor:var(--dag-connector);--borderRadius:1.25rem;box-sizing:border-box}.straightLine.svelte-1hyaq5f{position:absolute;top:0;bottom:0;left:0;right:0;border-left:var(--strokeWidth) solid var(--strokeColor)}.topLeft.svelte-1hyaq5f{position:absolute;top:0;left:0;right:50%;bottom:calc(var(--dag-gap) / 2 - var(--strokeWidth) / 2);border-radius:0 0 0 var(--borderRadius);border-left:var(--strokeWidth) solid var(--strokeColor);border-bottom:var(--strokeWidth) solid var(--strokeColor)}.bottomRight.svelte-1hyaq5f{position:absolute;top:calc(100% - (var(--dag-gap) / 2 + var(--strokeWidth) / 2));left:50%;right:0;bottom:0;border-radius:0 var(--borderRadius) 0 0;border-top:var(--strokeWidth) solid var(--strokeColor);border-right:var(--strokeWidth) solid var(--strokeColor)}
170
+ .wrapper.svelte-117ceti.svelte-117ceti{position:relative;z-index:1}.step.svelte-117ceti.svelte-117ceti{font-size:0.75rem;padding:0.5rem;color:var(--dk-grey)}.rectangle.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-static);border:1px solid var(--dag-border);box-sizing:border-box;position:relative;height:var(--dag-step-height);width:var(--dag-step-width)}.rectangle.error.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-error)}.rectangle.success.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-success)}.rectangle.running.svelte-117ceti.svelte-117ceti{background-color:var(--dag-bg-running)}.level.svelte-117ceti.svelte-117ceti{z-index:-1;filter:contrast(0.5);position:absolute}.inner.svelte-117ceti.svelte-117ceti{position:relative;height:100%;width:100%}.name.svelte-117ceti.svelte-117ceti{font-weight:bold;overflow:hidden;text-overflow:ellipsis;display:block}.description.svelte-117ceti.svelte-117ceti{position:absolute;max-height:4rem;bottom:0;left:0;right:0;overflow:hidden;-webkit-line-clamp:4;line-clamp:4;display:-webkit-box;-webkit-box-orient:vertical}.overflown.description.svelte-117ceti.svelte-117ceti{cursor:help}.current.svelte-117ceti .rectangle.svelte-117ceti{box-shadow:0 0 10px var(--dag-selected)}.levelstoshow.svelte-117ceti.svelte-117ceti{position:absolute;bottom:100%;right:0;font-size:0.75rem;font-weight:100;text-align:right}.connectorwrapper.svelte-1hyaq5f{transform-origin:0 0;position:absolute;z-index:0;min-width:var(--strokeWidth)}.flip.svelte-1hyaq5f{transform:scaleX(-1)}.path.svelte-1hyaq5f{--strokeWidth:0.5rem;--strokeColor:var(--dag-connector);--borderRadius:1.25rem;box-sizing:border-box}.straightLine.svelte-1hyaq5f{position:absolute;top:0;bottom:0;left:0;right:0;border-left:var(--strokeWidth) solid var(--strokeColor)}.topLeft.svelte-1hyaq5f{position:absolute;top:0;left:0;right:50%;bottom:calc(var(--dag-gap) / 2 - var(--strokeWidth) / 2);border-radius:0 0 0 var(--borderRadius);border-left:var(--strokeWidth) solid var(--strokeColor);border-bottom:var(--strokeWidth) solid var(--strokeColor)}.bottomRight.svelte-1hyaq5f{position:absolute;top:calc(100% - (var(--dag-gap) / 2 + var(--strokeWidth) / 2));left:50%;right:0;bottom:0;border-radius:0 var(--borderRadius) 0 0;border-top:var(--strokeWidth) solid var(--strokeColor);border-right:var(--strokeWidth) solid var(--strokeColor)}
@@ -295,6 +295,70 @@ class TaskToDict:
295
295
  def _parse_range(self, data_object):
296
296
  return self._get_repr().repr(data_object)
297
297
 
298
+ @staticmethod
299
+ def _parse_pandas_column(column_object):
300
+ # There are two types of parsing we do here.
301
+ # 1. We explicitly parse the types we know how to parse
302
+ # 2. We try to partially match a type name to the column's type.
303
+ # - We do this because `datetime64` can match `datetime64[ns]` and `datetime64[ns, UTC]`
304
+ # - We do this because period can match `period[D]` and `period[2D]` etc.
305
+ # - There are just too many types to explicitly parse so we go by this heuristic
306
+ # We have a default parser called `truncate_long_objects` which type casts any column to string
307
+ # and truncates it to 30 characters.
308
+ # If there is any form of TypeError or ValueError we set the column value to "Unsupported Type"
309
+ # We also set columns which are have null values to "null" strings
310
+ time_format = "%Y-%m-%dT%H:%M:%SZ"
311
+ truncate_long_objects = (
312
+ lambda x: x.astype("string").str.slice(0, 30) + "..."
313
+ if x.astype("string").str.len().max() > 30
314
+ else x.astype("string")
315
+ )
316
+ type_parser = {
317
+ "int64": lambda x: x,
318
+ "float64": lambda x: x,
319
+ "bool": lambda x: x,
320
+ "object": lambda x: truncate_long_objects(x.fillna("null")),
321
+ "category": truncate_long_objects,
322
+ }
323
+
324
+ partial_type_name_match_parsers = {
325
+ "complex": {
326
+ "complex": lambda x: x.astype("string"),
327
+ },
328
+ "datetime": {
329
+ "datetime64": lambda x: x.dt.strftime(time_format),
330
+ "timedelta": lambda x: x.dt.total_seconds(),
331
+ },
332
+ "interval": {
333
+ "interval": lambda x: x.astype("string"),
334
+ },
335
+ "period": {
336
+ "period": lambda x: x.astype("string"),
337
+ },
338
+ }
339
+
340
+ def _match_partial_type():
341
+ col_type = column_object.dtype
342
+ for _, type_parsers in partial_type_name_match_parsers.items():
343
+ for type_name, parser in type_parsers.items():
344
+ if type_name in str(col_type):
345
+ return parser(column_object)
346
+ return None
347
+
348
+ try:
349
+ col_type = str(column_object.dtype)
350
+ if col_type in type_parser:
351
+ return type_parser[col_type](column_object)
352
+ else:
353
+ parsed_col = _match_partial_type()
354
+ if parsed_col is not None:
355
+ return parsed_col
356
+ return truncate_long_objects(column_object)
357
+ except ValueError as e:
358
+ return "Unsupported type: {0}".format(col_type)
359
+ except TypeError as e:
360
+ return "Unsupported type: {0}".format(col_type)
361
+
298
362
  def _parse_pandas_dataframe(self, data_object, truncate=True):
299
363
  headers = list(data_object.columns)
300
364
  data = data_object
@@ -302,18 +366,16 @@ class TaskToDict:
302
366
  data = data_object.head()
303
367
  index_column = data.index
304
368
  time_format = "%Y-%m-%dT%H:%M:%SZ"
305
- if index_column.dtype == "datetime64[ns]":
369
+
370
+ if "datetime64" in str(index_column.dtype):
306
371
  if index_column.__class__.__name__ == "DatetimeIndex":
307
372
  index_column = index_column.strftime(time_format)
308
373
  else:
309
374
  index_column = index_column.dt.strftime(time_format)
310
375
 
311
376
  for col in data.columns:
312
- # we convert datetime columns to strings
313
- if data[col].dtype == "datetime64[ns]":
314
- data[col] = data[col].dt.strftime(time_format)
377
+ data[col] = self._parse_pandas_column(data[col])
315
378
 
316
- data = data.astype(object).where(data.notnull(), None)
317
379
  data_vals = data.values.tolist()
318
380
  for row, idx in zip(data_vals, index_column.values.tolist()):
319
381
  row.insert(0, idx)