ob-metaflow 2.11.14.1__tar.gz → 2.11.15.2__tar.gz

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 (317) hide show
  1. {ob_metaflow-2.11.14.1/ob_metaflow.egg-info → ob_metaflow-2.11.15.2}/PKG-INFO +2 -2
  2. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cli.py +0 -120
  3. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/datastore_set.py +1 -1
  4. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/flow_datastore.py +32 -6
  5. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/task_datastore.py +50 -0
  6. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_config.py +8 -0
  7. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_environment.py +1 -1
  8. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/__init__.py +5 -0
  9. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/airflow.py +4 -0
  10. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/argo_workflows.py +2 -0
  11. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/batch/batch_cli.py +6 -4
  12. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/azure_exceptions.py +1 -1
  13. ob_metaflow-2.11.15.2/metaflow/plugins/azure/azure_secret_manager_secrets_provider.py +236 -0
  14. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/s3/s3.py +8 -8
  15. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/kubernetes.py +4 -0
  16. ob_metaflow-2.11.15.2/metaflow/plugins/logs_cli.py +358 -0
  17. ob_metaflow-2.11.15.2/metaflow/version.py +1 -0
  18. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2/ob_metaflow.egg-info}/PKG-INFO +2 -2
  19. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/ob_metaflow.egg-info/SOURCES.txt +2 -0
  20. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/ob_metaflow.egg-info/requires.txt +1 -1
  21. ob_metaflow-2.11.14.1/metaflow/version.py +0 -1
  22. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/LICENSE +0 -0
  23. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/MANIFEST.in +0 -0
  24. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/README.md +0 -0
  25. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/R.py +0 -0
  26. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/__init__.py +0 -0
  27. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/__init__.py +0 -0
  28. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/__init__.py +0 -0
  29. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_bashcomplete.py +0 -0
  30. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_compat.py +0 -0
  31. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_termui_impl.py +0 -0
  32. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_textwrap.py +0 -0
  33. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_unicodefun.py +0 -0
  34. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/_winconsole.py +0 -0
  35. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/core.py +0 -0
  36. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/decorators.py +0 -0
  37. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/exceptions.py +0 -0
  38. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/formatting.py +0 -0
  39. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/globals.py +0 -0
  40. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/parser.py +0 -0
  41. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/termui.py +0 -0
  42. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/testing.py +0 -0
  43. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/types.py +0 -0
  44. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/click/utils.py +0 -0
  45. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/__init__.py +0 -0
  46. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_elffile.py +0 -0
  47. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_manylinux.py +0 -0
  48. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_musllinux.py +0 -0
  49. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_parser.py +0 -0
  50. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_structures.py +0 -0
  51. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/_tokenizer.py +0 -0
  52. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/markers.py +0 -0
  53. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/py.typed +0 -0
  54. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/requirements.py +0 -0
  55. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/specifiers.py +0 -0
  56. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/tags.py +0 -0
  57. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/utils.py +0 -0
  58. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/packaging/version.py +0 -0
  59. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_5/__init__.py +0 -0
  60. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -0
  61. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -0
  62. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_5/zipp.py +0 -0
  63. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/__init__.py +0 -0
  64. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/__init__.py +0 -0
  65. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_adapters.py +0 -0
  66. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_collections.py +0 -0
  67. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_compat.py +0 -0
  68. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_functools.py +0 -0
  69. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_itertools.py +0 -0
  70. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_meta.py +0 -0
  71. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/_text.py +0 -0
  72. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/importlib_metadata/py.typed +0 -0
  73. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/typing_extensions.py +0 -0
  74. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_6/zipp.py +0 -0
  75. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_7/__init__.py +0 -0
  76. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/_vendor/v3_7/zipp.py +0 -0
  77. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cards.py +0 -0
  78. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cli_args.py +0 -0
  79. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/client/__init__.py +0 -0
  80. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/client/core.py +0 -0
  81. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/client/filecache.py +0 -0
  82. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/clone_util.py +0 -0
  83. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/__init__.py +0 -0
  84. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/configure_cmd.py +0 -0
  85. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/develop/__init__.py +0 -0
  86. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/develop/stub_generator.py +0 -0
  87. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/develop/stubs.py +0 -0
  88. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/main_cli.py +0 -0
  89. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/tutorials_cmd.py +0 -0
  90. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd/util.py +0 -0
  91. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/cmd_with_io.py +0 -0
  92. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/__init__.py +0 -0
  93. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/content_addressed_store.py +0 -0
  94. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/datastore_storage.py +0 -0
  95. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/exceptions.py +0 -0
  96. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/datastore/inputs.py +0 -0
  97. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/debug.py +0 -0
  98. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/decorators.py +0 -0
  99. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/event_logger.py +0 -0
  100. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/events.py +0 -0
  101. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/exception.py +0 -0
  102. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/extension_support/__init__.py +0 -0
  103. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/extension_support/_empty_file.py +0 -0
  104. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/extension_support/cmd.py +0 -0
  105. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/extension_support/integrations.py +0 -0
  106. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/extension_support/plugins.py +0 -0
  107. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/flowspec.py +0 -0
  108. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/graph.py +0 -0
  109. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/includefile.py +0 -0
  110. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/integrations.py +0 -0
  111. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/lint.py +0 -0
  112. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metadata/__init__.py +0 -0
  113. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metadata/heartbeat.py +0 -0
  114. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metadata/metadata.py +0 -0
  115. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metadata/util.py +0 -0
  116. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_config_funcs.py +0 -0
  117. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_current.py +0 -0
  118. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_profile.py +0 -0
  119. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/metaflow_version.py +0 -0
  120. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/mflog/__init__.py +0 -0
  121. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/mflog/mflog.py +0 -0
  122. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/mflog/save_logs.py +0 -0
  123. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/mflog/save_logs_periodically.py +0 -0
  124. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/mflog/tee.py +0 -0
  125. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/monitor.py +0 -0
  126. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/multicore_utils.py +0 -0
  127. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/package.py +0 -0
  128. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/parameters.py +0 -0
  129. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/__init__.py +0 -0
  130. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/airflow_cli.py +0 -0
  131. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/airflow_decorator.py +0 -0
  132. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/airflow_utils.py +0 -0
  133. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/dag.py +0 -0
  134. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/exception.py +0 -0
  135. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/plumbing/__init__.py +0 -0
  136. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/plumbing/set_parameters.py +0 -0
  137. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/sensors/__init__.py +0 -0
  138. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/sensors/base_sensor.py +0 -0
  139. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/sensors/external_task_sensor.py +0 -0
  140. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/airflow/sensors/s3_sensor.py +0 -0
  141. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/__init__.py +0 -0
  142. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/argo_client.py +0 -0
  143. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/argo_events.py +0 -0
  144. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/argo_workflows_cli.py +0 -0
  145. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/argo_workflows_decorator.py +0 -0
  146. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/argo/generate_input_paths.py +0 -0
  147. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/__init__.py +0 -0
  148. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/aws_client.py +0 -0
  149. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/aws_utils.py +0 -0
  150. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/batch/__init__.py +0 -0
  151. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/batch/batch.py +0 -0
  152. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/batch/batch_client.py +0 -0
  153. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/batch/batch_decorator.py +0 -0
  154. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/secrets_manager/__init__.py +0 -0
  155. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/secrets_manager/aws_secrets_manager_secrets_provider.py +0 -0
  156. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/__init__.py +0 -0
  157. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/dynamo_db_client.py +0 -0
  158. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/event_bridge_client.py +0 -0
  159. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/production_token.py +0 -0
  160. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/schedule_decorator.py +0 -0
  161. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/set_batch_environment.py +0 -0
  162. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/step_functions.py +0 -0
  163. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/step_functions_cli.py +0 -0
  164. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/step_functions_client.py +0 -0
  165. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/aws/step_functions/step_functions_decorator.py +0 -0
  166. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/__init__.py +0 -0
  167. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/azure_credential.py +0 -0
  168. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/azure_tail.py +0 -0
  169. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/azure_utils.py +0 -0
  170. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/blob_service_client_factory.py +0 -0
  171. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/azure/includefile_support.py +0 -0
  172. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/__init__.py +0 -0
  173. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_cli.py +0 -0
  174. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_client.py +0 -0
  175. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_creator.py +0 -0
  176. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_datastore.py +0 -0
  177. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_decorator.py +0 -0
  178. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/__init__.py +0 -0
  179. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/base.html +0 -0
  180. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/basic.py +0 -0
  181. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/bundle.css +0 -0
  182. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/card.py +0 -0
  183. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/chevron/__init__.py +0 -0
  184. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/chevron/main.py +0 -0
  185. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/chevron/metadata.py +0 -0
  186. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/chevron/renderer.py +0 -0
  187. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/chevron/tokenizer.py +0 -0
  188. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/components.py +0 -0
  189. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/convert_to_native_type.py +0 -0
  190. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/main.js +0 -0
  191. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/renderer_tools.py +0 -0
  192. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_modules/test_cards.py +0 -0
  193. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_resolver.py +0 -0
  194. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_server.py +0 -0
  195. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/card_viewer/viewer.html +0 -0
  196. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/component_serializer.py +0 -0
  197. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/cards/exception.py +0 -0
  198. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/catch_decorator.py +0 -0
  199. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datastores/__init__.py +0 -0
  200. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datastores/azure_storage.py +0 -0
  201. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datastores/gs_storage.py +0 -0
  202. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datastores/local_storage.py +0 -0
  203. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datastores/s3_storage.py +0 -0
  204. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/__init__.py +0 -0
  205. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/local.py +0 -0
  206. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/s3/__init__.py +0 -0
  207. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/s3/s3op.py +0 -0
  208. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/s3/s3tail.py +0 -0
  209. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/datatools/s3/s3util.py +0 -0
  210. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/debug_logger.py +0 -0
  211. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/debug_monitor.py +0 -0
  212. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/__init__.py +0 -0
  213. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/client.py +0 -0
  214. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/client_modules.py +0 -0
  215. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/communication/__init__.py +0 -0
  216. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/communication/bytestream.py +0 -0
  217. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/communication/channel.py +0 -0
  218. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/communication/socket_bytestream.py +0 -0
  219. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/communication/utils.py +0 -0
  220. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/configurations/emulate_test_lib/__init__.py +0 -0
  221. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/configurations/emulate_test_lib/overrides.py +0 -0
  222. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/configurations/emulate_test_lib/server_mappings.py +0 -0
  223. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/configurations/test_lib_impl/__init__.py +0 -0
  224. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/configurations/test_lib_impl/test_lib.py +0 -0
  225. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/consts.py +0 -0
  226. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/data_transferer.py +0 -0
  227. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/exception_transferer.py +0 -0
  228. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/override_decorators.py +0 -0
  229. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/server.py +0 -0
  230. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/stub.py +0 -0
  231. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/env_escape/utils.py +0 -0
  232. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/environment_decorator.py +0 -0
  233. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/events_decorator.py +0 -0
  234. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/frameworks/__init__.py +0 -0
  235. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/frameworks/pytorch.py +0 -0
  236. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/__init__.py +0 -0
  237. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +0 -0
  238. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/gs_exceptions.py +0 -0
  239. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/gs_storage_client_factory.py +0 -0
  240. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/gs_tail.py +0 -0
  241. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/gs_utils.py +0 -0
  242. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/gcp/includefile_support.py +0 -0
  243. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/__init__.py +0 -0
  244. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/kubernetes_cli.py +0 -0
  245. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/kubernetes_client.py +0 -0
  246. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/kubernetes_decorator.py +0 -0
  247. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/kubernetes/kubernetes_job.py +0 -0
  248. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/metadata/__init__.py +0 -0
  249. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/metadata/local.py +0 -0
  250. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/metadata/service.py +0 -0
  251. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/package_cli.py +0 -0
  252. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/parallel_decorator.py +0 -0
  253. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/project_decorator.py +0 -0
  254. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/__init__.py +0 -0
  255. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/bootstrap.py +0 -0
  256. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/conda_decorator.py +0 -0
  257. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/conda_environment.py +0 -0
  258. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/micromamba.py +0 -0
  259. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/pip.py +0 -0
  260. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/pypi_decorator.py +0 -0
  261. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/pypi_environment.py +0 -0
  262. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/pypi/utils.py +0 -0
  263. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/resources_decorator.py +0 -0
  264. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/retry_decorator.py +0 -0
  265. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/secrets/__init__.py +0 -0
  266. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/secrets/inline_secrets_provider.py +0 -0
  267. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/secrets/secrets_decorator.py +0 -0
  268. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/storage_executor.py +0 -0
  269. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/tag_cli.py +0 -0
  270. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/test_unbounded_foreach_decorator.py +0 -0
  271. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/plugins/timeout_decorator.py +0 -0
  272. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/procpoll.py +0 -0
  273. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/py.typed +0 -0
  274. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/pylint_wrapper.py +0 -0
  275. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/runtime.py +0 -0
  276. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/sidecar/__init__.py +0 -0
  277. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/sidecar/sidecar.py +0 -0
  278. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/sidecar/sidecar_messages.py +0 -0
  279. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/sidecar/sidecar_subprocess.py +0 -0
  280. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/sidecar/sidecar_worker.py +0 -0
  281. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tagging_util.py +0 -0
  282. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/task.py +0 -0
  283. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tracing/__init__.py +0 -0
  284. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tracing/propagator.py +0 -0
  285. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tracing/span_exporter.py +0 -0
  286. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tracing/tracing_modules.py +0 -0
  287. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/00-helloworld/README.md +0 -0
  288. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/00-helloworld/helloworld.py +0 -0
  289. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/01-playlist/README.md +0 -0
  290. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/01-playlist/movies.csv +0 -0
  291. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/01-playlist/playlist.ipynb +0 -0
  292. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/01-playlist/playlist.py +0 -0
  293. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/02-statistics/README.md +0 -0
  294. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/02-statistics/movies.csv +0 -0
  295. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/02-statistics/stats.ipynb +0 -0
  296. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/02-statistics/stats.py +0 -0
  297. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/03-playlist-redux/README.md +0 -0
  298. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/03-playlist-redux/playlist.py +0 -0
  299. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/04-playlist-plus/README.md +0 -0
  300. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/04-playlist-plus/playlist.py +0 -0
  301. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/05-hello-cloud/README.md +0 -0
  302. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/05-hello-cloud/hello-cloud.ipynb +0 -0
  303. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/05-hello-cloud/hello-cloud.py +0 -0
  304. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/06-statistics-redux/README.md +0 -0
  305. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/06-statistics-redux/stats.ipynb +0 -0
  306. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/07-worldview/README.md +0 -0
  307. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/07-worldview/worldview.ipynb +0 -0
  308. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/08-autopilot/README.md +0 -0
  309. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/tutorials/08-autopilot/autopilot.ipynb +0 -0
  310. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/unbounded_foreach.py +0 -0
  311. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/util.py +0 -0
  312. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/metaflow/vendor.py +0 -0
  313. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/ob_metaflow.egg-info/dependency_links.txt +0 -0
  314. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/ob_metaflow.egg-info/entry_points.txt +0 -0
  315. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/ob_metaflow.egg-info/top_level.txt +0 -0
  316. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/setup.cfg +0 -0
  317. {ob_metaflow-2.11.14.1 → ob_metaflow-2.11.15.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow
3
- Version: 2.11.14.1
3
+ Version: 2.11.15.2
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: ob-metaflow-stubs==2.11.14.1; extra == "stubs"
15
+ Requires-Dist: ob-metaflow-stubs==2.11.15.2; extra == "stubs"
16
16
 
17
17
  ![Metaflow_Logo_Horizontal_FullColor_Ribbon_Dark_RGB](https://user-images.githubusercontent.com/763451/89453116-96a57e00-d713-11ea-9fa6-82b29d4d6eff.png)
18
18
 
@@ -287,126 +287,6 @@ def dump(obj, input_path, private=None, max_value_size=None, include=None, file=
287
287
  echo("Artifacts written to *%s*" % file)
288
288
 
289
289
 
290
- @cli.command(
291
- help="Show stdout/stderr produced by a task or all tasks in a step. "
292
- "The format for input-path is either <run_id>/<step_name> or "
293
- "<run_id>/<step_name>/<task_id>."
294
- )
295
- @click.argument("input-path")
296
- @click.option(
297
- "--stdout/--no-stdout",
298
- default=False,
299
- show_default=True,
300
- help="Show stdout of the task.",
301
- )
302
- @click.option(
303
- "--stderr/--no-stderr",
304
- default=False,
305
- show_default=True,
306
- help="Show stderr of the task.",
307
- )
308
- @click.option(
309
- "--both/--no-both",
310
- default=True,
311
- show_default=True,
312
- help="Show both stdout and stderr of the task.",
313
- )
314
- @click.option(
315
- "--timestamps/--no-timestamps",
316
- default=False,
317
- show_default=True,
318
- help="Show timestamps.",
319
- )
320
- @click.pass_obj
321
- def logs(obj, input_path, stdout=None, stderr=None, both=None, timestamps=False):
322
- types = set()
323
- if stdout:
324
- types.add("stdout")
325
- both = False
326
- if stderr:
327
- types.add("stderr")
328
- both = False
329
- if both:
330
- types.update(("stdout", "stderr"))
331
-
332
- streams = list(sorted(types, reverse=True))
333
-
334
- # Pathspec can either be run_id/step_name or run_id/step_name/task_id.
335
- parts = input_path.split("/")
336
- if len(parts) == 2:
337
- run_id, step_name = parts
338
- task_id = None
339
- elif len(parts) == 3:
340
- run_id, step_name, task_id = parts
341
- else:
342
- raise CommandException(
343
- "input_path should either be run_id/step_name "
344
- "or run_id/step_name/task_id"
345
- )
346
-
347
- datastore_set = TaskDataStoreSet(
348
- obj.flow_datastore, run_id, steps=[step_name], allow_not_done=True
349
- )
350
- if task_id:
351
- ds_list = [
352
- TaskDataStore(
353
- obj.flow_datastore,
354
- run_id=run_id,
355
- step_name=step_name,
356
- task_id=task_id,
357
- mode="r",
358
- allow_not_done=True,
359
- )
360
- ]
361
- else:
362
- ds_list = list(datastore_set) # get all tasks
363
-
364
- if ds_list:
365
-
366
- def echo_unicode(line, **kwargs):
367
- click.secho(line.decode("UTF-8", errors="replace"), **kwargs)
368
-
369
- # old style logs are non mflog-style logs
370
- maybe_old_style = True
371
- for ds in ds_list:
372
- echo(
373
- "Dumping logs of run_id=*{run_id}* "
374
- "step=*{step}* task_id=*{task_id}*".format(
375
- run_id=ds.run_id, step=ds.step_name, task_id=ds.task_id
376
- ),
377
- fg="magenta",
378
- )
379
-
380
- for stream in streams:
381
- echo(stream, bold=True)
382
- logs = ds.load_logs(LOG_SOURCES, stream)
383
- if any(data for _, data in logs):
384
- # attempt to read new, mflog-style logs
385
- for line in mflog.merge_logs([blob for _, blob in logs]):
386
- if timestamps:
387
- ts = mflog.utc_to_local(line.utc_tstamp)
388
- tstamp = ts.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
389
- click.secho(tstamp + " ", fg=LOGGER_TIMESTAMP, nl=False)
390
- echo_unicode(line.msg)
391
- maybe_old_style = False
392
- elif maybe_old_style:
393
- # if they are not available, we may be looking at
394
- # a legacy run (unless we have seen new-style data already
395
- # for another stream). This return an empty string if
396
- # nothing is found
397
- log = ds.load_log_legacy(stream)
398
- if log and timestamps:
399
- raise CommandException(
400
- "We can't show --timestamps for old runs. Sorry!"
401
- )
402
- echo_unicode(log, nl=False)
403
- else:
404
- raise CommandException(
405
- "No Tasks found at the given path -- "
406
- "either none exist or none have started yet"
407
- )
408
-
409
-
410
290
  # TODO - move step and init under a separate 'internal' subcommand
411
291
 
412
292
 
@@ -22,7 +22,7 @@ class TaskDataStoreSet(object):
22
22
  prefetch_data_artifacts=None,
23
23
  allow_not_done=False,
24
24
  ):
25
- self.task_datastores = flow_datastore.get_latest_task_datastores(
25
+ self.task_datastores = flow_datastore.get_task_datastores(
26
26
  run_id, steps=steps, pathspecs=pathspecs, allow_not_done=allow_not_done
27
27
  )
28
28
 
@@ -67,8 +67,15 @@ class FlowDataStore(object):
67
67
  def datastore_root(self):
68
68
  return self._storage_impl.datastore_root
69
69
 
70
- def get_latest_task_datastores(
71
- self, run_id=None, steps=None, pathspecs=None, allow_not_done=False
70
+ def get_task_datastores(
71
+ self,
72
+ run_id=None,
73
+ steps=None,
74
+ pathspecs=None,
75
+ allow_not_done=False,
76
+ attempt=None,
77
+ include_prior=False,
78
+ mode="r",
72
79
  ):
73
80
  """
74
81
  Return a list of TaskDataStore for a subset of the tasks.
@@ -93,6 +100,12 @@ class FlowDataStore(object):
93
100
  allow_not_done : bool, optional
94
101
  If True, returns the latest attempt of a task even if that attempt
95
102
  wasn't marked as done, by default False
103
+ attempt : int, optional
104
+ Attempt number of the tasks to return. If not provided, returns latest attempt.
105
+ include_prior : boolean, default False
106
+ If True, returns all attempts up to and including attempt.
107
+ mode : str, default "r"
108
+ Mode to initialize the returned TaskDataStores in.
96
109
 
97
110
  Returns
98
111
  -------
@@ -126,8 +139,13 @@ class FlowDataStore(object):
126
139
  if task.is_file is False
127
140
  ]
128
141
  urls = []
142
+ # parse content urls for specific attempt only, or for all attempts in max range
143
+ attempt_range = range(metaflow_config.MAX_ATTEMPTS)
144
+ # we have no reason to check for attempts greater than MAX_ATTEMPTS, as they do not exist.
145
+ if attempt is not None and attempt <= metaflow_config.MAX_ATTEMPTS - 1:
146
+ attempt_range = range(attempt + 1) if include_prior else [attempt]
129
147
  for task_url in task_urls:
130
- for attempt in range(metaflow_config.MAX_ATTEMPTS):
148
+ for attempt in attempt_range:
131
149
  for suffix in [
132
150
  TaskDataStore.METADATA_DATA_SUFFIX,
133
151
  TaskDataStore.METADATA_ATTEMPT_SUFFIX,
@@ -168,11 +186,19 @@ class FlowDataStore(object):
168
186
  for (run, step, task), attempt in latest_started_attempts.items()
169
187
  )
170
188
  if allow_not_done:
171
- latest_to_fetch = latest_started_attempts
189
+ latest_to_fetch = (
190
+ done_attempts.union(latest_started_attempts)
191
+ if include_prior
192
+ else latest_started_attempts
193
+ )
172
194
  else:
173
- latest_to_fetch = latest_started_attempts & done_attempts
195
+ latest_to_fetch = (
196
+ done_attempts
197
+ if include_prior
198
+ else (latest_started_attempts & done_attempts)
199
+ )
174
200
  latest_to_fetch = [
175
- (v[0], v[1], v[2], v[3], data_objs.get(v), "r", allow_not_done)
201
+ (v[0], v[1], v[2], v[3], data_objs.get(v), mode, allow_not_done)
176
202
  for v in latest_to_fetch
177
203
  ]
178
204
  return list(itertools.starmap(self.get_task_datastore, latest_to_fetch))
@@ -173,6 +173,26 @@ class TaskDataStore(object):
173
173
  if data_obj is not None:
174
174
  self._objects = data_obj.get("objects", {})
175
175
  self._info = data_obj.get("info", {})
176
+ elif self._mode == "d":
177
+ self._objects = {}
178
+ self._info = {}
179
+
180
+ if self._attempt is None:
181
+ for i in range(metaflow_config.MAX_ATTEMPTS):
182
+ check_meta = self._metadata_name_for_attempt(
183
+ self.METADATA_ATTEMPT_SUFFIX, i
184
+ )
185
+ if self.has_metadata(check_meta, add_attempt=False):
186
+ self._attempt = i
187
+
188
+ # Do not allow destructive operations on the datastore if attempt is still in flight
189
+ # and we explicitly did not allow operating on running tasks.
190
+ if not allow_not_done and not self.has_metadata(self.METADATA_DONE_SUFFIX):
191
+ raise DataException(
192
+ "No completed attempts of the task was found for task '%s'"
193
+ % self._path
194
+ )
195
+
176
196
  else:
177
197
  raise DataException("Unknown datastore mode: '%s'" % self._mode)
178
198
 
@@ -750,6 +770,36 @@ class TaskDataStore(object):
750
770
  to_store_dict[n] = data
751
771
  self._save_file(to_store_dict)
752
772
 
773
+ @require_mode("d")
774
+ def scrub_logs(self, logsources, stream, attempt_override=None):
775
+ path_logsources = {
776
+ self._metadata_name_for_attempt(
777
+ self._get_log_location(s, stream),
778
+ attempt_override=attempt_override,
779
+ ): s
780
+ for s in logsources
781
+ }
782
+
783
+ # Legacy log paths
784
+ legacy_log = self._metadata_name_for_attempt(
785
+ "%s.log" % stream, attempt_override
786
+ )
787
+ path_logsources[legacy_log] = stream
788
+
789
+ existing_paths = [
790
+ path
791
+ for path in path_logsources.keys()
792
+ if self.has_metadata(path, add_attempt=False)
793
+ ]
794
+
795
+ # Replace log contents with [REDACTED source stream]
796
+ to_store_dict = {
797
+ path: bytes("[REDACTED %s %s]" % (path_logsources[path], stream), "utf-8")
798
+ for path in existing_paths
799
+ }
800
+
801
+ self._save_file(to_store_dict, add_attempt=False, allow_overwrite=True)
802
+
753
803
  @require_mode("r")
754
804
  def load_log_legacy(self, stream, attempt_override=None):
755
805
  """
@@ -154,6 +154,13 @@ AWS_SECRETS_MANAGER_DEFAULT_REGION = from_conf("AWS_SECRETS_MANAGER_DEFAULT_REGI
154
154
  # - "projects/1234567890/secrets/foo-" -> "projects/1234567890/secrets/foo-mysecret"
155
155
  GCP_SECRET_MANAGER_PREFIX = from_conf("GCP_SECRET_MANAGER_PREFIX")
156
156
 
157
+ # Secrets Backend - Azure Key Vault prefix. With this, users don't have to
158
+ # specify the full https:// vault url in the @secret decorator.
159
+ #
160
+ # It does not make a difference if the prefix ends in a / or not. We will handle either
161
+ # case correctly.
162
+ AZURE_KEY_VAULT_PREFIX = from_conf("AZURE_KEY_VAULT_PREFIX")
163
+
157
164
  # The root directory to save artifact pulls in, when using S3 or Azure
158
165
  ARTIFACT_LOCALROOT = from_conf("ARTIFACT_LOCALROOT", os.getcwd())
159
166
 
@@ -471,6 +478,7 @@ def get_pinned_conda_libs(python_version, datastore_type):
471
478
  elif datastore_type == "azure":
472
479
  pins["azure-identity"] = ">=1.10.0"
473
480
  pins["azure-storage-blob"] = ">=12.12.0"
481
+ pins["azure-keyvault-secrets"] = ">=4.8.0"
474
482
  elif datastore_type == "gs":
475
483
  pins["google-cloud-storage"] = ">=2.5.0"
476
484
  pins["google-auth"] = ">=2.11.0"
@@ -124,7 +124,7 @@ class MetaflowEnvironment(object):
124
124
  cmds.append("%s -m pip install awscli boto3" % self._python())
125
125
  elif datastore_type == "azure":
126
126
  cmds.append(
127
- "%s -m pip install azure-identity azure-storage-blob simple-azure-blob-downloader -qqq"
127
+ "%s -m pip install azure-identity azure-storage-blob azure-keyvault-secrets simple-azure-blob-downloader -qqq"
128
128
  % self._python()
129
129
  )
130
130
  elif datastore_type == "gs":
@@ -14,6 +14,7 @@ CLIS_DESC = [
14
14
  ("argo-workflows", ".argo.argo_workflows_cli.cli"),
15
15
  ("card", ".cards.card_cli.cli"),
16
16
  ("tag", ".tag_cli.cli"),
17
+ ("logs", ".logs_cli.cli"),
17
18
  ]
18
19
 
19
20
  from .test_unbounded_foreach_decorator import InternalTestUnboundedForeachInput
@@ -124,6 +125,10 @@ SECRETS_PROVIDERS_DESC = [
124
125
  "gcp-secret-manager",
125
126
  ".gcp.gcp_secret_manager_secrets_provider.GcpSecretManagerSecretsProvider",
126
127
  ),
128
+ (
129
+ "az-key-vault",
130
+ ".azure.azure_secret_manager_secrets_provider.AzureKeyVaultSecretsProvider",
131
+ ),
127
132
  ]
128
133
 
129
134
  AZURE_CLIENT_PROVIDERS_DESC = [
@@ -32,6 +32,7 @@ from metaflow.metaflow_config import (
32
32
  S3_ENDPOINT_URL,
33
33
  SERVICE_HEADERS,
34
34
  SERVICE_INTERNAL_URL,
35
+ AZURE_KEY_VAULT_PREFIX,
35
36
  )
36
37
 
37
38
  from metaflow.metaflow_config_funcs import config_values
@@ -412,6 +413,9 @@ class Airflow(object):
412
413
  if GCP_SECRET_MANAGER_PREFIX:
413
414
  env["METAFLOW_GCP_SECRET_MANAGER_PREFIX"] = GCP_SECRET_MANAGER_PREFIX
414
415
 
416
+ if AZURE_KEY_VAULT_PREFIX:
417
+ env["METAFLOW_AZURE_KEY_VAULT_PREFIX"] = AZURE_KEY_VAULT_PREFIX
418
+
415
419
  env.update(additional_mf_variables)
416
420
 
417
421
  service_account = (
@@ -33,6 +33,7 @@ from metaflow.metaflow_config import (
33
33
  DEFAULT_METADATA,
34
34
  DEFAULT_SECRETS_BACKEND_TYPE,
35
35
  GCP_SECRET_MANAGER_PREFIX,
36
+ AZURE_KEY_VAULT_PREFIX,
36
37
  KUBERNETES_FETCH_EC2_METADATA,
37
38
  KUBERNETES_LABELS,
38
39
  KUBERNETES_NAMESPACE,
@@ -1420,6 +1421,7 @@ class ArgoWorkflows(object):
1420
1421
  "METAFLOW_AWS_SECRETS_MANAGER_DEFAULT_REGION"
1421
1422
  ] = AWS_SECRETS_MANAGER_DEFAULT_REGION
1422
1423
  env["METAFLOW_GCP_SECRET_MANAGER_PREFIX"] = GCP_SECRET_MANAGER_PREFIX
1424
+ env["METAFLOW_AZURE_KEY_VAULT_PREFIX"] = AZURE_KEY_VAULT_PREFIX
1423
1425
 
1424
1426
  # support for Azure
1425
1427
  env[
@@ -10,7 +10,7 @@ from metaflow.exception import CommandException, METAFLOW_EXIT_DISALLOW_RETRY
10
10
  from metaflow.metadata.util import sync_local_metadata_from_datastore
11
11
  from metaflow.metaflow_config import DATASTORE_LOCAL_DIR
12
12
  from metaflow.mflog import TASK_LOG_SOURCE
13
-
13
+ from metaflow.unbounded_foreach import UBF_CONTROL, UBF_TASK
14
14
  from .batch import Batch, BatchKilledException
15
15
 
16
16
 
@@ -150,8 +150,10 @@ def kill(ctx, run_id, user, my_runs):
150
150
  @click.option("--tmpfs-tempdir", is_flag=True, help="tmpfs requirement for AWS Batch.")
151
151
  @click.option("--tmpfs-size", help="tmpfs requirement for AWS Batch.")
152
152
  @click.option("--tmpfs-path", help="tmpfs requirement for AWS Batch.")
153
- # TODO: Maybe remove it altogether since it's not used here
154
- @click.option("--ubf-context", default=None, type=click.Choice([None, "ubf_control"]))
153
+ # NOTE: ubf-context is not explicitly used, but @parallel decorator tries to pass this so keep it for now
154
+ @click.option(
155
+ "--ubf-context", default=None, type=click.Choice(["none", UBF_CONTROL, UBF_TASK])
156
+ )
155
157
  @click.option("--host-volumes", multiple=True)
156
158
  @click.option("--efs-volumes", multiple=True)
157
159
  @click.option(
@@ -344,7 +346,7 @@ def step(
344
346
  log_options=log_options,
345
347
  num_parallel=num_parallel,
346
348
  )
347
- except Exception as e:
349
+ except Exception:
348
350
  traceback.print_exc()
349
351
  _sync_metadata()
350
352
  sys.exit(METAFLOW_EXIT_DISALLOW_RETRY)
@@ -10,4 +10,4 @@ class MetaflowAzureResourceError(MetaflowException):
10
10
 
11
11
 
12
12
  class MetaflowAzurePackageError(MetaflowException):
13
- headline = "Missing required packages 'azure-identity' and 'azure-storage-blob'"
13
+ headline = "Missing required packages 'azure-identity' and 'azure-storage-blob' and 'azure-keyvault-secrets'"
@@ -0,0 +1,236 @@
1
+ from metaflow.plugins.secrets import SecretsProvider
2
+ import re
3
+ import base64
4
+ import codecs
5
+ from urllib.parse import urlparse
6
+ from metaflow.exception import MetaflowException
7
+ import sys
8
+ from metaflow.metaflow_config import AZURE_KEY_VAULT_PREFIX
9
+ from metaflow.plugins.azure.azure_credential import (
10
+ create_cacheable_azure_credential,
11
+ )
12
+
13
+
14
+ class MetaflowAzureKeyVaultBadVault(MetaflowException):
15
+ """Raised when the secretid is fully qualified but does not have the right key vault domain"""
16
+
17
+
18
+ class MetaflowAzureKeyVaultBadSecretType(MetaflowException):
19
+ """Raised when the secret type is anything except secrets"""
20
+
21
+
22
+ class MetaflowAzureKeyVaultBadSecretPath(MetaflowException):
23
+ """Raised when the secret path does not match to expected length"""
24
+
25
+
26
+ class MetaflowAzureKeyVaultBadSecretName(MetaflowException):
27
+ """Raised when the secret name does not match expected pattern"""
28
+
29
+
30
+ class MetaflowAzureKeyVaultBadSecretVersion(MetaflowException):
31
+ """Raised when the secret version does not match expected pattern"""
32
+
33
+
34
+ class MetaflowAzureKeyVaultBadSecret(MetaflowException):
35
+ """Raised when the secret does not match supported patterns in Metaflow"""
36
+
37
+
38
+ class AzureKeyVaultSecretsProvider(SecretsProvider):
39
+ TYPE = "az-key-vault"
40
+ key_vault_domains = [
41
+ ".vault.azure.net",
42
+ ".vault.azure.cn",
43
+ ".vault.usgovcloudapi.net",
44
+ ".vault.microsoftazure.de",
45
+ ]
46
+ supported_vault_object_types = ["secrets"]
47
+
48
+ # https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates has details on vault name structure
49
+ # Vault name and Managed HSM pool name must be a 3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -.
50
+ def _is_valid_vault_name(self, vault_name):
51
+ vault_name_pattern = r"^(?!.*--)[a-zA-Z0-9-]{3,24}$"
52
+ return re.match(vault_name_pattern, vault_name) is not None
53
+
54
+ # The type of the object can be, "keys", "secrets", or "certificates".
55
+ # Currently only secrets will be supported
56
+ def _is_valid_object_type(self, secret_type):
57
+ for type in self.supported_vault_object_types:
58
+ if secret_type == type:
59
+ return True
60
+ return False
61
+
62
+ # The secret name must be a 1-127 character string, starting with a letter and containing only 0-9, a-z, A-Z, and -.
63
+ def _is_valid_secret_name(self, secret_name):
64
+ secret_name_pattern = r"^[a-zA-Z][a-zA-Z0-9-]{0,126}$"
65
+ return re.match(secret_name_pattern, secret_name) is not None
66
+
67
+ # An object-version is a system-generated, 32 character string identifier that is optionally used to address a unique version of an object.
68
+ def _is_valid_object_version(self, secret_version):
69
+ object_version_pattern = r"^[a-zA-Z0-9]{32}$"
70
+ return re.match(object_version_pattern, secret_version) is not None
71
+
72
+ # This function will check if the secret_id is fully qualified url. It will return True iff the secret_id is of the form:
73
+ # https://myvault.vault.azure.net/secrets/mysecret/ec96f02080254f109c51a1f14cdb1931 OR
74
+ # https://myvault.vault.azure.net/secrets/mysecret/
75
+ # validating the above as per recommendations in https://devblogs.microsoft.com/azure-sdk/guidance-for-applications-using-the-key-vault-libraries/
76
+ def _is_secret_id_fully_qualified_url(self, secret_id):
77
+ # if the secret_id is None/empty/does not start with https then return false
78
+ if secret_id is None or secret_id == "" or not secret_id.startswith("https://"):
79
+ return False
80
+ try:
81
+ parsed_vault_url = urlparse(secret_id)
82
+ except ValueError:
83
+ print(f"invalid vault url", file=sys.stderr)
84
+ return False
85
+ hostname = parsed_vault_url.netloc
86
+
87
+ k_v_domain_found = False
88
+ actual_k_v_domain = ""
89
+ for k_v_domain in self.key_vault_domains:
90
+ if k_v_domain in hostname:
91
+ k_v_domain_found = True
92
+ actual_k_v_domain = k_v_domain
93
+ break
94
+ if not k_v_domain_found:
95
+ # the secret_id started with https:// however the key_vault_domains
96
+ # were not present in the secret_id which means
97
+ raise MetaflowAzureKeyVaultBadVault(f"bad key vault domain {secret_id}")
98
+
99
+ # given the secret_id seems to have a valid key vault domain
100
+ # lets verify that the vault name corresponds to its regex.
101
+ vault_name = hostname[: -len(actual_k_v_domain)]
102
+ # verify the vault name pattern
103
+ if not self._is_valid_vault_name(vault_name):
104
+ raise MetaflowAzureKeyVaultBadVault(f"bad key vault name {vault_name}")
105
+
106
+ path_parts = parsed_vault_url.path.strip("/").split("/")
107
+ total_path_parts = len(path_parts)
108
+ if total_path_parts < 2 or total_path_parts > 3:
109
+ raise MetaflowAzureKeyVaultBadSecretPath(
110
+ f"bad secret uri path {path_parts}"
111
+ )
112
+
113
+ object_type = path_parts[0]
114
+ if not self._is_valid_object_type(object_type):
115
+ raise MetaflowAzureKeyVaultBadSecretType(f"bad secret type {object_type}")
116
+
117
+ secret_name = path_parts[1]
118
+ if not self._is_valid_secret_name(secret_name=secret_name):
119
+ raise MetaflowAzureKeyVaultBadSecretName(f"bad secret name {secret_name}")
120
+
121
+ if total_path_parts == 3:
122
+ if not self._is_valid_object_version(path_parts[2]):
123
+ raise MetaflowAzureKeyVaultBadSecretVersion(
124
+ f"bad secret version {path_parts[2]}"
125
+ )
126
+
127
+ return True
128
+
129
+ # This function will validate the correctness of the partial secret id.
130
+ # It will attempt to construct the fully qualified secret URL internally and
131
+ # call the _is_secret_id_fully_qualified_url to check validity
132
+ def _is_partial_secret_valid(self, secret_id):
133
+ secret_parts = secret_id.strip("/").split("/")
134
+ total_secret_parts = len(secret_parts)
135
+ if total_secret_parts < 1 or total_secret_parts > 2:
136
+ return False
137
+
138
+ # since the secret_id is supposedly a partial id, the AZURE_KEY_VAULT_PREFIX
139
+ # must be set.
140
+ if not AZURE_KEY_VAULT_PREFIX:
141
+ raise ValueError(
142
+ f"cannot use simple secret id without setting METAFLOW_AZURE_KEY_VAULT_PREFIX. {AZURE_KEY_VAULT_PREFIX}"
143
+ )
144
+ domain = AZURE_KEY_VAULT_PREFIX.rstrip("/")
145
+ full_secret = f"{domain}/secrets/{secret_id}"
146
+ if not self._is_secret_id_fully_qualified_url(full_secret):
147
+ return False
148
+
149
+ return True
150
+
151
+ def _sanitize_key_as_env_var(self, key):
152
+ """
153
+ Sanitize a key as an environment variable name.
154
+ This is purely a convenience trade-off to cover common cases well, vs. introducing
155
+ ambiguities (e.g. did the final '_' come from '.', or '-' or is original?).
156
+
157
+ 1/27/2023(jackie):
158
+
159
+ We start with few rules and should *sparingly* add more over time.
160
+ Also, it's TBD whether all possible providers will share the same sanitization logic.
161
+ Therefore we will keep this function private for now
162
+ """
163
+ return key.replace("-", "_").replace(".", "_").replace("/", "_")
164
+
165
+ def get_secret_as_dict(self, secret_id, options={}, role=None):
166
+ # https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references?tabs=azure-cli has a lot of details on
167
+ # the patterns used in key vault
168
+ # Vault names and Managed HSM pool names are selected by the user and are globally unique.
169
+ # Vault name and Managed HSM pool name must be a 3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -.
170
+ # object-type The type of the object. As of 05/08/24 only "secrets", are supported
171
+ # object-name An object-name is a user provided name for and must be unique within a key vault. The name must be a 1-127 character string, starting with a letter and containing only 0-9, a-z, A-Z, and -.
172
+ # object-version An object-version is a system-generated, 32 character string identifier that is optionally used to address a unique version of an object.
173
+
174
+ # We allow these forms of secret_id:
175
+ #
176
+ # 1. Full path like https://<key-vault-name><.vault-domain>/secrets/<secret-name>/<secret-version>. This is what you
177
+ # see in Azure portal and is easy to copy paste.
178
+ #
179
+ # 2. Full path but without the version like https://<key-vault-name><.vault-domain>/secrets/<secret-name>
180
+ #
181
+ # 3. Simple string like mysecret. This corresponds to the SecretName.
182
+ #
183
+ # 4. Simple string with <secret-name>/<secret-version> suffix like mysecret/123
184
+
185
+ # The latter two forms require METAFLOW_AZURE_KEY_VAULT_PREFIX to be set.
186
+
187
+ # if the secret_id is None/empty/does not start with https then return false
188
+ if secret_id is None or secret_id == "":
189
+ raise MetaflowAzureKeyVaultBadSecret(f"empty secret id is not supported")
190
+
191
+ # check if the passed in secret is a short-form ( #3/#4 in the above comment)
192
+ if not secret_id.startswith("https://"):
193
+ # check if the secret_id is of form `secret_name` OR `secret_name/secret_version`
194
+ if not self._is_partial_secret_valid(secret_id=secret_id):
195
+ raise MetaflowAzureKeyVaultBadSecret(
196
+ f"unsupported partial secret {secret_id}"
197
+ )
198
+
199
+ domain = AZURE_KEY_VAULT_PREFIX.rstrip("/")
200
+ full_secret = f"{domain}/secrets/{secret_id}"
201
+
202
+ # if the secret id is passed as a URL - then check if the url is fully qualified
203
+ if secret_id.startswith("https://"):
204
+ if not self._is_secret_id_fully_qualified_url(secret_id=secret_id):
205
+ raise MetaflowException(f"unsupported secret {secret_id}")
206
+ full_secret = secret_id
207
+
208
+ # at this point I know that the secret URL is good so we can start creating the Secret Client
209
+ az_credentials = create_cacheable_azure_credential()
210
+ res = urlparse(full_secret)
211
+ az_vault_url = f"{res.scheme}://{res.netloc}" # https://myvault.vault.azure.net
212
+ secret_data = res.path.strip("/").split("/")[1:]
213
+ secret_name = secret_data[0]
214
+ secret_version = None
215
+ if len(secret_data) > 1:
216
+ secret_version = secret_data[1]
217
+
218
+ from azure.keyvault.secrets import SecretClient
219
+
220
+ client = SecretClient(vault_url=az_vault_url, credential=az_credentials)
221
+
222
+ key_vault_secret_val = client.get_secret(
223
+ name=secret_name, version=secret_version
224
+ )
225
+
226
+ result = {}
227
+
228
+ if options.get("env_var_name") is not None:
229
+ env_var_name = options["env_var_name"]
230
+ sanitized_key = self._sanitize_key_as_env_var(env_var_name)
231
+ else:
232
+ sanitized_key = self._sanitize_key_as_env_var(key_vault_secret_val.name)
233
+
234
+ response_payload = key_vault_secret_val.value
235
+ result[sanitized_key] = response_payload
236
+ return result