ob-metaflow 2.15.13.1__py2.py3-none-any.whl → 2.19.7.1rc0__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 (169) hide show
  1. metaflow/__init__.py +10 -3
  2. metaflow/_vendor/imghdr/__init__.py +186 -0
  3. metaflow/_vendor/yaml/__init__.py +427 -0
  4. metaflow/_vendor/yaml/composer.py +139 -0
  5. metaflow/_vendor/yaml/constructor.py +748 -0
  6. metaflow/_vendor/yaml/cyaml.py +101 -0
  7. metaflow/_vendor/yaml/dumper.py +62 -0
  8. metaflow/_vendor/yaml/emitter.py +1137 -0
  9. metaflow/_vendor/yaml/error.py +75 -0
  10. metaflow/_vendor/yaml/events.py +86 -0
  11. metaflow/_vendor/yaml/loader.py +63 -0
  12. metaflow/_vendor/yaml/nodes.py +49 -0
  13. metaflow/_vendor/yaml/parser.py +589 -0
  14. metaflow/_vendor/yaml/reader.py +185 -0
  15. metaflow/_vendor/yaml/representer.py +389 -0
  16. metaflow/_vendor/yaml/resolver.py +227 -0
  17. metaflow/_vendor/yaml/scanner.py +1435 -0
  18. metaflow/_vendor/yaml/serializer.py +111 -0
  19. metaflow/_vendor/yaml/tokens.py +104 -0
  20. metaflow/cards.py +4 -0
  21. metaflow/cli.py +125 -21
  22. metaflow/cli_components/init_cmd.py +1 -0
  23. metaflow/cli_components/run_cmds.py +204 -40
  24. metaflow/cli_components/step_cmd.py +160 -4
  25. metaflow/client/__init__.py +1 -0
  26. metaflow/client/core.py +198 -130
  27. metaflow/client/filecache.py +59 -32
  28. metaflow/cmd/code/__init__.py +2 -1
  29. metaflow/cmd/develop/stub_generator.py +49 -18
  30. metaflow/cmd/develop/stubs.py +9 -27
  31. metaflow/cmd/make_wrapper.py +30 -0
  32. metaflow/datastore/__init__.py +1 -0
  33. metaflow/datastore/content_addressed_store.py +40 -9
  34. metaflow/datastore/datastore_set.py +10 -1
  35. metaflow/datastore/flow_datastore.py +124 -4
  36. metaflow/datastore/spin_datastore.py +91 -0
  37. metaflow/datastore/task_datastore.py +92 -6
  38. metaflow/debug.py +5 -0
  39. metaflow/decorators.py +331 -82
  40. metaflow/extension_support/__init__.py +414 -356
  41. metaflow/extension_support/_empty_file.py +2 -2
  42. metaflow/flowspec.py +322 -82
  43. metaflow/graph.py +178 -15
  44. metaflow/includefile.py +25 -3
  45. metaflow/lint.py +94 -3
  46. metaflow/meta_files.py +13 -0
  47. metaflow/metadata_provider/metadata.py +13 -2
  48. metaflow/metaflow_config.py +66 -4
  49. metaflow/metaflow_environment.py +91 -25
  50. metaflow/metaflow_profile.py +18 -0
  51. metaflow/metaflow_version.py +16 -1
  52. metaflow/package/__init__.py +673 -0
  53. metaflow/packaging_sys/__init__.py +880 -0
  54. metaflow/packaging_sys/backend.py +128 -0
  55. metaflow/packaging_sys/distribution_support.py +153 -0
  56. metaflow/packaging_sys/tar_backend.py +99 -0
  57. metaflow/packaging_sys/utils.py +54 -0
  58. metaflow/packaging_sys/v1.py +527 -0
  59. metaflow/parameters.py +6 -2
  60. metaflow/plugins/__init__.py +6 -0
  61. metaflow/plugins/airflow/airflow.py +11 -1
  62. metaflow/plugins/airflow/airflow_cli.py +16 -5
  63. metaflow/plugins/argo/argo_client.py +42 -20
  64. metaflow/plugins/argo/argo_events.py +6 -6
  65. metaflow/plugins/argo/argo_workflows.py +1023 -344
  66. metaflow/plugins/argo/argo_workflows_cli.py +396 -94
  67. metaflow/plugins/argo/argo_workflows_decorator.py +9 -0
  68. metaflow/plugins/argo/argo_workflows_deployer_objects.py +75 -49
  69. metaflow/plugins/argo/capture_error.py +5 -2
  70. metaflow/plugins/argo/conditional_input_paths.py +35 -0
  71. metaflow/plugins/argo/exit_hooks.py +209 -0
  72. metaflow/plugins/argo/param_val.py +19 -0
  73. metaflow/plugins/aws/aws_client.py +6 -0
  74. metaflow/plugins/aws/aws_utils.py +33 -1
  75. metaflow/plugins/aws/batch/batch.py +72 -5
  76. metaflow/plugins/aws/batch/batch_cli.py +24 -3
  77. metaflow/plugins/aws/batch/batch_decorator.py +57 -6
  78. metaflow/plugins/aws/step_functions/step_functions.py +28 -3
  79. metaflow/plugins/aws/step_functions/step_functions_cli.py +49 -4
  80. metaflow/plugins/aws/step_functions/step_functions_deployer.py +3 -0
  81. metaflow/plugins/aws/step_functions/step_functions_deployer_objects.py +30 -0
  82. metaflow/plugins/cards/card_cli.py +20 -1
  83. metaflow/plugins/cards/card_creator.py +24 -1
  84. metaflow/plugins/cards/card_datastore.py +21 -49
  85. metaflow/plugins/cards/card_decorator.py +58 -6
  86. metaflow/plugins/cards/card_modules/basic.py +38 -9
  87. metaflow/plugins/cards/card_modules/bundle.css +1 -1
  88. metaflow/plugins/cards/card_modules/chevron/renderer.py +1 -1
  89. metaflow/plugins/cards/card_modules/components.py +592 -3
  90. metaflow/plugins/cards/card_modules/convert_to_native_type.py +34 -5
  91. metaflow/plugins/cards/card_modules/json_viewer.py +232 -0
  92. metaflow/plugins/cards/card_modules/main.css +1 -0
  93. metaflow/plugins/cards/card_modules/main.js +56 -41
  94. metaflow/plugins/cards/card_modules/test_cards.py +22 -6
  95. metaflow/plugins/cards/component_serializer.py +1 -8
  96. metaflow/plugins/cards/metadata.py +22 -0
  97. metaflow/plugins/catch_decorator.py +9 -0
  98. metaflow/plugins/datastores/local_storage.py +12 -6
  99. metaflow/plugins/datastores/spin_storage.py +12 -0
  100. metaflow/plugins/datatools/s3/s3.py +49 -17
  101. metaflow/plugins/datatools/s3/s3op.py +113 -66
  102. metaflow/plugins/env_escape/client_modules.py +102 -72
  103. metaflow/plugins/events_decorator.py +127 -121
  104. metaflow/plugins/exit_hook/__init__.py +0 -0
  105. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  106. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  107. metaflow/plugins/kubernetes/kubernetes.py +12 -1
  108. metaflow/plugins/kubernetes/kubernetes_cli.py +11 -0
  109. metaflow/plugins/kubernetes/kubernetes_decorator.py +25 -6
  110. metaflow/plugins/kubernetes/kubernetes_job.py +12 -4
  111. metaflow/plugins/kubernetes/kubernetes_jobsets.py +31 -30
  112. metaflow/plugins/metadata_providers/local.py +76 -82
  113. metaflow/plugins/metadata_providers/service.py +13 -9
  114. metaflow/plugins/metadata_providers/spin.py +16 -0
  115. metaflow/plugins/package_cli.py +36 -24
  116. metaflow/plugins/parallel_decorator.py +11 -2
  117. metaflow/plugins/parsers.py +16 -0
  118. metaflow/plugins/pypi/bootstrap.py +7 -1
  119. metaflow/plugins/pypi/conda_decorator.py +41 -82
  120. metaflow/plugins/pypi/conda_environment.py +14 -6
  121. metaflow/plugins/pypi/micromamba.py +9 -1
  122. metaflow/plugins/pypi/pip.py +41 -5
  123. metaflow/plugins/pypi/pypi_decorator.py +4 -4
  124. metaflow/plugins/pypi/utils.py +22 -0
  125. metaflow/plugins/secrets/__init__.py +3 -0
  126. metaflow/plugins/secrets/secrets_decorator.py +14 -178
  127. metaflow/plugins/secrets/secrets_func.py +49 -0
  128. metaflow/plugins/secrets/secrets_spec.py +101 -0
  129. metaflow/plugins/secrets/utils.py +74 -0
  130. metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
  131. metaflow/plugins/timeout_decorator.py +0 -1
  132. metaflow/plugins/uv/bootstrap.py +29 -1
  133. metaflow/plugins/uv/uv_environment.py +5 -3
  134. metaflow/pylint_wrapper.py +5 -1
  135. metaflow/runner/click_api.py +79 -26
  136. metaflow/runner/deployer.py +208 -6
  137. metaflow/runner/deployer_impl.py +32 -12
  138. metaflow/runner/metaflow_runner.py +266 -33
  139. metaflow/runner/subprocess_manager.py +21 -1
  140. metaflow/runner/utils.py +27 -16
  141. metaflow/runtime.py +660 -66
  142. metaflow/task.py +255 -26
  143. metaflow/user_configs/config_options.py +33 -21
  144. metaflow/user_configs/config_parameters.py +220 -58
  145. metaflow/user_decorators/__init__.py +0 -0
  146. metaflow/user_decorators/common.py +144 -0
  147. metaflow/user_decorators/mutable_flow.py +512 -0
  148. metaflow/user_decorators/mutable_step.py +424 -0
  149. metaflow/user_decorators/user_flow_decorator.py +264 -0
  150. metaflow/user_decorators/user_step_decorator.py +749 -0
  151. metaflow/util.py +197 -7
  152. metaflow/vendor.py +23 -7
  153. metaflow/version.py +1 -1
  154. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Makefile +13 -2
  155. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/Tiltfile +107 -7
  156. {ob_metaflow-2.15.13.1.data → ob_metaflow-2.19.7.1rc0.data}/data/share/metaflow/devtools/pick_services.sh +1 -0
  157. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/METADATA +2 -3
  158. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/RECORD +162 -121
  159. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/WHEEL +1 -1
  160. metaflow/_vendor/v3_5/__init__.py +0 -1
  161. metaflow/_vendor/v3_5/importlib_metadata/__init__.py +0 -644
  162. metaflow/_vendor/v3_5/importlib_metadata/_compat.py +0 -152
  163. metaflow/_vendor/v3_5/zipp.py +0 -329
  164. metaflow/info_file.py +0 -25
  165. metaflow/package.py +0 -203
  166. metaflow/user_configs/config_decorators.py +0 -568
  167. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/entry_points.txt +0 -0
  168. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/licenses/LICENSE +0 -0
  169. {ob_metaflow-2.15.13.1.dist-info → ob_metaflow-2.19.7.1rc0.dist-info}/top_level.txt +0 -0
@@ -1,152 +0,0 @@
1
- from __future__ import absolute_import, unicode_literals
2
-
3
- import io
4
- import abc
5
- import sys
6
- import email
7
-
8
-
9
- if sys.version_info > (3,): # pragma: nocover
10
- import builtins
11
- from configparser import ConfigParser
12
- import contextlib
13
- FileNotFoundError = builtins.FileNotFoundError
14
- IsADirectoryError = builtins.IsADirectoryError
15
- NotADirectoryError = builtins.NotADirectoryError
16
- PermissionError = builtins.PermissionError
17
- map = builtins.map
18
- from itertools import filterfalse
19
- else: # pragma: nocover
20
- from backports.configparser import ConfigParser
21
- from itertools import imap as map # type: ignore
22
- from itertools import ifilterfalse as filterfalse
23
- import contextlib2 as contextlib
24
- FileNotFoundError = IOError, OSError
25
- IsADirectoryError = IOError, OSError
26
- NotADirectoryError = IOError, OSError
27
- PermissionError = IOError, OSError
28
-
29
- str = type('')
30
-
31
- suppress = contextlib.suppress
32
-
33
- if sys.version_info > (3, 5): # pragma: nocover
34
- import pathlib
35
- else: # pragma: nocover
36
- import pathlib2 as pathlib
37
-
38
- try:
39
- ModuleNotFoundError = builtins.FileNotFoundError
40
- except (NameError, AttributeError): # pragma: nocover
41
- ModuleNotFoundError = ImportError # type: ignore
42
-
43
-
44
- if sys.version_info >= (3,): # pragma: nocover
45
- from importlib.abc import MetaPathFinder
46
- else: # pragma: nocover
47
- class MetaPathFinder(object):
48
- __metaclass__ = abc.ABCMeta
49
-
50
-
51
- __metaclass__ = type
52
- __all__ = [
53
- 'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError',
54
- 'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError',
55
- 'NotADirectoryError', 'email_message_from_string',
56
- ]
57
-
58
-
59
- def install(cls):
60
- """
61
- Class decorator for installation on sys.meta_path.
62
-
63
- Adds the backport DistributionFinder to sys.meta_path and
64
- attempts to disable the finder functionality of the stdlib
65
- DistributionFinder.
66
- """
67
- sys.meta_path.append(cls())
68
- disable_stdlib_finder()
69
- return cls
70
-
71
-
72
- def disable_stdlib_finder():
73
- """
74
- Give the backport primacy for discovering path-based distributions
75
- by monkey-patching the stdlib O_O.
76
-
77
- See #91 for more background for rationale on this sketchy
78
- behavior.
79
- """
80
- def matches(finder):
81
- return (
82
- getattr(finder, '__module__', None) == '_frozen_importlib_external'
83
- and hasattr(finder, 'find_distributions')
84
- )
85
- for finder in filter(matches, sys.meta_path): # pragma: nocover
86
- del finder.find_distributions
87
-
88
-
89
- class NullFinder:
90
- """
91
- A "Finder" (aka "MetaClassFinder") that never finds any modules,
92
- but may find distributions.
93
- """
94
- @staticmethod
95
- def find_spec(*args, **kwargs):
96
- return None
97
-
98
- # In Python 2, the import system requires finders
99
- # to have a find_module() method, but this usage
100
- # is deprecated in Python 3 in favor of find_spec().
101
- # For the purposes of this finder (i.e. being present
102
- # on sys.meta_path but having no other import
103
- # system functionality), the two methods are identical.
104
- find_module = find_spec
105
-
106
-
107
- def py2_message_from_string(text): # nocoverpy3
108
- # Work around https://bugs.python.org/issue25545 where
109
- # email.message_from_string cannot handle Unicode on Python 2.
110
- io_buffer = io.StringIO(text)
111
- return email.message_from_file(io_buffer)
112
-
113
-
114
- email_message_from_string = (
115
- py2_message_from_string
116
- if sys.version_info < (3,) else
117
- email.message_from_string
118
- )
119
-
120
-
121
- class PyPy_repr:
122
- """
123
- Override repr for EntryPoint objects on PyPy to avoid __iter__ access.
124
- Ref #97, #102.
125
- """
126
- affected = hasattr(sys, 'pypy_version_info')
127
-
128
- def __compat_repr__(self): # pragma: nocover
129
- def make_param(name):
130
- value = getattr(self, name)
131
- return '{name}={value!r}'.format(**locals())
132
- params = ', '.join(map(make_param, self._fields))
133
- return 'EntryPoint({params})'.format(**locals())
134
-
135
- if affected: # pragma: nocover
136
- __repr__ = __compat_repr__
137
- del affected
138
-
139
-
140
- # from itertools recipes
141
- def unique_everseen(iterable): # pragma: nocover
142
- "List unique elements, preserving order. Remember all elements ever seen."
143
- seen = set()
144
- seen_add = seen.add
145
-
146
- for element in filterfalse(seen.__contains__, iterable):
147
- seen_add(element)
148
- yield element
149
-
150
-
151
- unique_ordered = (
152
- unique_everseen if sys.version_info < (3, 7) else dict.fromkeys)
@@ -1,329 +0,0 @@
1
- import io
2
- import posixpath
3
- import zipfile
4
- import itertools
5
- import contextlib
6
- import sys
7
- import pathlib
8
-
9
- if sys.version_info < (3, 7):
10
- from collections import OrderedDict
11
- else:
12
- OrderedDict = dict
13
-
14
-
15
- __all__ = ['Path']
16
-
17
-
18
- def _parents(path):
19
- """
20
- Given a path with elements separated by
21
- posixpath.sep, generate all parents of that path.
22
-
23
- >>> list(_parents('b/d'))
24
- ['b']
25
- >>> list(_parents('/b/d/'))
26
- ['/b']
27
- >>> list(_parents('b/d/f/'))
28
- ['b/d', 'b']
29
- >>> list(_parents('b'))
30
- []
31
- >>> list(_parents(''))
32
- []
33
- """
34
- return itertools.islice(_ancestry(path), 1, None)
35
-
36
-
37
- def _ancestry(path):
38
- """
39
- Given a path with elements separated by
40
- posixpath.sep, generate all elements of that path
41
-
42
- >>> list(_ancestry('b/d'))
43
- ['b/d', 'b']
44
- >>> list(_ancestry('/b/d/'))
45
- ['/b/d', '/b']
46
- >>> list(_ancestry('b/d/f/'))
47
- ['b/d/f', 'b/d', 'b']
48
- >>> list(_ancestry('b'))
49
- ['b']
50
- >>> list(_ancestry(''))
51
- []
52
- """
53
- path = path.rstrip(posixpath.sep)
54
- while path and path != posixpath.sep:
55
- yield path
56
- path, tail = posixpath.split(path)
57
-
58
-
59
- _dedupe = OrderedDict.fromkeys
60
- """Deduplicate an iterable in original order"""
61
-
62
-
63
- def _difference(minuend, subtrahend):
64
- """
65
- Return items in minuend not in subtrahend, retaining order
66
- with O(1) lookup.
67
- """
68
- return itertools.filterfalse(set(subtrahend).__contains__, minuend)
69
-
70
-
71
- class CompleteDirs(zipfile.ZipFile):
72
- """
73
- A ZipFile subclass that ensures that implied directories
74
- are always included in the namelist.
75
- """
76
-
77
- @staticmethod
78
- def _implied_dirs(names):
79
- parents = itertools.chain.from_iterable(map(_parents, names))
80
- as_dirs = (p + posixpath.sep for p in parents)
81
- return _dedupe(_difference(as_dirs, names))
82
-
83
- def namelist(self):
84
- names = super(CompleteDirs, self).namelist()
85
- return names + list(self._implied_dirs(names))
86
-
87
- def _name_set(self):
88
- return set(self.namelist())
89
-
90
- def resolve_dir(self, name):
91
- """
92
- If the name represents a directory, return that name
93
- as a directory (with the trailing slash).
94
- """
95
- names = self._name_set()
96
- dirname = name + '/'
97
- dir_match = name not in names and dirname in names
98
- return dirname if dir_match else name
99
-
100
- @classmethod
101
- def make(cls, source):
102
- """
103
- Given a source (filename or zipfile), return an
104
- appropriate CompleteDirs subclass.
105
- """
106
- if isinstance(source, CompleteDirs):
107
- return source
108
-
109
- if not isinstance(source, zipfile.ZipFile):
110
- return cls(_pathlib_compat(source))
111
-
112
- # Only allow for FastLookup when supplied zipfile is read-only
113
- if 'r' not in source.mode:
114
- cls = CompleteDirs
115
-
116
- source.__class__ = cls
117
- return source
118
-
119
-
120
- class FastLookup(CompleteDirs):
121
- """
122
- ZipFile subclass to ensure implicit
123
- dirs exist and are resolved rapidly.
124
- """
125
-
126
- def namelist(self):
127
- with contextlib.suppress(AttributeError):
128
- return self.__names
129
- self.__names = super(FastLookup, self).namelist()
130
- return self.__names
131
-
132
- def _name_set(self):
133
- with contextlib.suppress(AttributeError):
134
- return self.__lookup
135
- self.__lookup = super(FastLookup, self)._name_set()
136
- return self.__lookup
137
-
138
-
139
- def _pathlib_compat(path):
140
- """
141
- For path-like objects, convert to a filename for compatibility
142
- on Python 3.6.1 and earlier.
143
- """
144
- try:
145
- return path.__fspath__()
146
- except AttributeError:
147
- return str(path)
148
-
149
-
150
- class Path:
151
- """
152
- A pathlib-compatible interface for zip files.
153
-
154
- Consider a zip file with this structure::
155
-
156
- .
157
- ├── a.txt
158
- └── b
159
- ├── c.txt
160
- └── d
161
- └── e.txt
162
-
163
- >>> data = io.BytesIO()
164
- >>> zf = zipfile.ZipFile(data, 'w')
165
- >>> zf.writestr('a.txt', 'content of a')
166
- >>> zf.writestr('b/c.txt', 'content of c')
167
- >>> zf.writestr('b/d/e.txt', 'content of e')
168
- >>> zf.filename = 'mem/abcde.zip'
169
-
170
- Path accepts the zipfile object itself or a filename
171
-
172
- >>> root = Path(zf)
173
-
174
- From there, several path operations are available.
175
-
176
- Directory iteration (including the zip file itself):
177
-
178
- >>> a, b = root.iterdir()
179
- >>> a
180
- Path('mem/abcde.zip', 'a.txt')
181
- >>> b
182
- Path('mem/abcde.zip', 'b/')
183
-
184
- name property:
185
-
186
- >>> b.name
187
- 'b'
188
-
189
- join with divide operator:
190
-
191
- >>> c = b / 'c.txt'
192
- >>> c
193
- Path('mem/abcde.zip', 'b/c.txt')
194
- >>> c.name
195
- 'c.txt'
196
-
197
- Read text:
198
-
199
- >>> c.read_text()
200
- 'content of c'
201
-
202
- existence:
203
-
204
- >>> c.exists()
205
- True
206
- >>> (b / 'missing.txt').exists()
207
- False
208
-
209
- Coercion to string:
210
-
211
- >>> import os
212
- >>> str(c).replace(os.sep, posixpath.sep)
213
- 'mem/abcde.zip/b/c.txt'
214
-
215
- At the root, ``name``, ``filename``, and ``parent``
216
- resolve to the zipfile. Note these attributes are not
217
- valid and will raise a ``ValueError`` if the zipfile
218
- has no filename.
219
-
220
- >>> root.name
221
- 'abcde.zip'
222
- >>> str(root.filename).replace(os.sep, posixpath.sep)
223
- 'mem/abcde.zip'
224
- >>> str(root.parent)
225
- 'mem'
226
- """
227
-
228
- __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})"
229
-
230
- def __init__(self, root, at=""):
231
- """
232
- Construct a Path from a ZipFile or filename.
233
-
234
- Note: When the source is an existing ZipFile object,
235
- its type (__class__) will be mutated to a
236
- specialized type. If the caller wishes to retain the
237
- original type, the caller should either create a
238
- separate ZipFile object or pass a filename.
239
- """
240
- self.root = FastLookup.make(root)
241
- self.at = at
242
-
243
- def open(self, mode='r', *args, pwd=None, **kwargs):
244
- """
245
- Open this entry as text or binary following the semantics
246
- of ``pathlib.Path.open()`` by passing arguments through
247
- to io.TextIOWrapper().
248
- """
249
- if self.is_dir():
250
- raise IsADirectoryError(self)
251
- zip_mode = mode[0]
252
- if not self.exists() and zip_mode == 'r':
253
- raise FileNotFoundError(self)
254
- stream = self.root.open(self.at, zip_mode, pwd=pwd)
255
- if 'b' in mode:
256
- if args or kwargs:
257
- raise ValueError("encoding args invalid for binary operation")
258
- return stream
259
- return io.TextIOWrapper(stream, *args, **kwargs)
260
-
261
- @property
262
- def name(self):
263
- return pathlib.Path(self.at).name or self.filename.name
264
-
265
- @property
266
- def suffix(self):
267
- return pathlib.Path(self.at).suffix or self.filename.suffix
268
-
269
- @property
270
- def suffixes(self):
271
- return pathlib.Path(self.at).suffixes or self.filename.suffixes
272
-
273
- @property
274
- def stem(self):
275
- return pathlib.Path(self.at).stem or self.filename.stem
276
-
277
- @property
278
- def filename(self):
279
- return pathlib.Path(self.root.filename).joinpath(self.at)
280
-
281
- def read_text(self, *args, **kwargs):
282
- with self.open('r', *args, **kwargs) as strm:
283
- return strm.read()
284
-
285
- def read_bytes(self):
286
- with self.open('rb') as strm:
287
- return strm.read()
288
-
289
- def _is_child(self, path):
290
- return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/")
291
-
292
- def _next(self, at):
293
- return self.__class__(self.root, at)
294
-
295
- def is_dir(self):
296
- return not self.at or self.at.endswith("/")
297
-
298
- def is_file(self):
299
- return self.exists() and not self.is_dir()
300
-
301
- def exists(self):
302
- return self.at in self.root._name_set()
303
-
304
- def iterdir(self):
305
- if not self.is_dir():
306
- raise ValueError("Can't listdir a file")
307
- subs = map(self._next, self.root.namelist())
308
- return filter(self._is_child, subs)
309
-
310
- def __str__(self):
311
- return posixpath.join(self.root.filename, self.at)
312
-
313
- def __repr__(self):
314
- return self.__repr.format(self=self)
315
-
316
- def joinpath(self, *other):
317
- next = posixpath.join(self.at, *map(_pathlib_compat, other))
318
- return self._next(self.root.resolve_dir(next))
319
-
320
- __truediv__ = joinpath
321
-
322
- @property
323
- def parent(self):
324
- if not self.at:
325
- return self.filename.parent
326
- parent_at = posixpath.dirname(self.at.rstrip('/'))
327
- if parent_at:
328
- parent_at += '/'
329
- return self._next(parent_at)
metaflow/info_file.py DELETED
@@ -1,25 +0,0 @@
1
- import json
2
-
3
- from os import path
4
-
5
- CURRENT_DIRECTORY = path.dirname(path.abspath(__file__))
6
- INFO_FILE = path.join(path.dirname(CURRENT_DIRECTORY), "INFO")
7
-
8
- _info_file_content = None
9
- _info_file_present = None
10
-
11
-
12
- def read_info_file():
13
- global _info_file_content
14
- global _info_file_present
15
- if _info_file_present is None:
16
- _info_file_present = path.exists(INFO_FILE)
17
- if _info_file_present:
18
- try:
19
- with open(INFO_FILE, "r", encoding="utf-8") as contents:
20
- _info_file_content = json.load(contents)
21
- except IOError:
22
- pass
23
- if _info_file_present:
24
- return _info_file_content
25
- return None
metaflow/package.py DELETED
@@ -1,203 +0,0 @@
1
- import importlib
2
- import os
3
- import sys
4
- import tarfile
5
- import time
6
- import json
7
- from io import BytesIO
8
-
9
- from .user_configs.config_parameters import CONFIG_FILE, dump_config_values
10
- from .extension_support import EXT_PKG, package_mfext_all
11
- from .metaflow_config import DEFAULT_PACKAGE_SUFFIXES
12
- from .exception import MetaflowException
13
- from .util import to_unicode
14
- from . import R
15
- from .info_file import INFO_FILE
16
-
17
- DEFAULT_SUFFIXES_LIST = DEFAULT_PACKAGE_SUFFIXES.split(",")
18
- METAFLOW_SUFFIXES_LIST = [".py", ".html", ".css", ".js"]
19
-
20
-
21
- class NonUniqueFileNameToFilePathMappingException(MetaflowException):
22
- headline = "Non Unique file path for a file name included in code package"
23
-
24
- def __init__(self, filename, file_paths, lineno=None):
25
- msg = (
26
- "Filename %s included in the code package includes multiple different paths for the same name : %s.\n"
27
- "The `filename` in the `add_to_package` decorator hook requires a unique `file_path` to `file_name` mapping"
28
- % (filename, ", ".join(file_paths))
29
- )
30
- super().__init__(msg=msg, lineno=lineno)
31
-
32
-
33
- # this is os.walk(follow_symlinks=True) with cycle detection
34
- def walk_without_cycles(top_root):
35
- seen = set()
36
-
37
- def _recurse(root):
38
- for parent, dirs, files in os.walk(root):
39
- for d in dirs:
40
- path = os.path.join(parent, d)
41
- if os.path.islink(path):
42
- # Breaking loops: never follow the same symlink twice
43
- #
44
- # NOTE: this also means that links to sibling links are
45
- # not followed. In this case:
46
- #
47
- # x -> y
48
- # y -> oo
49
- # oo/real_file
50
- #
51
- # real_file is only included twice, not three times
52
- reallink = os.path.realpath(path)
53
- if reallink not in seen:
54
- seen.add(reallink)
55
- for x in _recurse(path):
56
- yield x
57
- yield parent, files
58
-
59
- for x in _recurse(top_root):
60
- yield x
61
-
62
-
63
- class MetaflowPackage(object):
64
- def __init__(self, flow, environment, echo, suffixes=DEFAULT_SUFFIXES_LIST):
65
- self.suffixes = list(set().union(suffixes, DEFAULT_SUFFIXES_LIST))
66
- self.environment = environment
67
- self.metaflow_root = os.path.dirname(__file__)
68
-
69
- self.flow_name = flow.name
70
- self._flow = flow
71
- self.create_time = time.time()
72
- environment.init_environment(echo)
73
- for step in flow:
74
- for deco in step.decorators:
75
- deco.package_init(flow, step.__name__, environment)
76
- self.blob = self._make()
77
-
78
- def _walk(self, root, exclude_hidden=True, suffixes=None):
79
- if suffixes is None:
80
- suffixes = []
81
- root = to_unicode(root) # handle files/folder with non ascii chars
82
- prefixlen = len("%s/" % os.path.dirname(root))
83
- for (
84
- path,
85
- files,
86
- ) in walk_without_cycles(root):
87
- if exclude_hidden and "/." in path:
88
- continue
89
- # path = path[2:] # strip the ./ prefix
90
- # if path and (path[0] == '.' or './' in path):
91
- # continue
92
- for fname in files:
93
- if (fname[0] == "." and fname in suffixes) or (
94
- fname[0] != "."
95
- and any(fname.endswith(suffix) for suffix in suffixes)
96
- ):
97
- p = os.path.join(path, fname)
98
- yield p, p[prefixlen:]
99
-
100
- def path_tuples(self):
101
- """
102
- Returns list of (path, arcname) to be added to the job package, where
103
- `arcname` is the alternative name for the file in the package.
104
- """
105
- # We want the following contents in the tarball
106
- # Metaflow package itself
107
- for path_tuple in self._walk(
108
- self.metaflow_root, exclude_hidden=False, suffixes=METAFLOW_SUFFIXES_LIST
109
- ):
110
- yield path_tuple
111
-
112
- # Metaflow extensions; for now, we package *all* extensions but this may change
113
- # at a later date; it is possible to call `package_mfext_package` instead of
114
- # `package_mfext_all` but in that case, make sure to also add a
115
- # metaflow_extensions/__init__.py file to properly "close" the metaflow_extensions
116
- # package and prevent other extensions from being loaded that may be
117
- # present in the rest of the system
118
- for path_tuple in package_mfext_all():
119
- yield path_tuple
120
-
121
- # Any custom packages exposed via decorators
122
- deco_module_paths = {}
123
- for step in self._flow:
124
- for deco in step.decorators:
125
- for path_tuple in deco.add_to_package():
126
- file_path, file_name = path_tuple
127
- # Check if the path is not duplicated as
128
- # many steps can have the same packages being imported
129
- if file_name not in deco_module_paths:
130
- deco_module_paths[file_name] = file_path
131
- yield path_tuple
132
- elif deco_module_paths[file_name] != file_path:
133
- raise NonUniqueFileNameToFilePathMappingException(
134
- file_name, [deco_module_paths[file_name], file_path]
135
- )
136
-
137
- # the package folders for environment
138
- for path_tuple in self.environment.add_to_package():
139
- yield path_tuple
140
- if R.use_r():
141
- # the R working directory
142
- for path_tuple in self._walk(
143
- "%s/" % R.working_dir(), suffixes=self.suffixes
144
- ):
145
- yield path_tuple
146
- # the R package
147
- for path_tuple in R.package_paths():
148
- yield path_tuple
149
- else:
150
- # the user's working directory
151
- flowdir = os.path.dirname(os.path.abspath(sys.argv[0])) + "/"
152
- for path_tuple in self._walk(flowdir, suffixes=self.suffixes):
153
- yield path_tuple
154
-
155
- def _add_configs(self, tar):
156
- buf = BytesIO()
157
- buf.write(json.dumps(dump_config_values(self._flow)).encode("utf-8"))
158
- self._add_file(tar, os.path.basename(CONFIG_FILE), buf)
159
-
160
- def _add_info(self, tar):
161
- buf = BytesIO()
162
- buf.write(
163
- json.dumps(
164
- self.environment.get_environment_info(include_ext_info=True)
165
- ).encode("utf-8")
166
- )
167
- self._add_file(tar, os.path.basename(INFO_FILE), buf)
168
-
169
- @staticmethod
170
- def _add_file(tar, filename, buf):
171
- info = tarfile.TarInfo(filename)
172
- buf.seek(0)
173
- info.size = len(buf.getvalue())
174
- # Setting this default to Dec 3, 2019
175
- info.mtime = 1575360000
176
- tar.addfile(info, buf)
177
-
178
- def _make(self):
179
- def no_mtime(tarinfo):
180
- # a modification time change should not change the hash of
181
- # the package. Only content modifications will.
182
- # Setting this default to Dec 3, 2019
183
- tarinfo.mtime = 1575360000
184
- return tarinfo
185
-
186
- buf = BytesIO()
187
- with tarfile.open(
188
- fileobj=buf, mode="w:gz", compresslevel=3, dereference=True
189
- ) as tar:
190
- self._add_info(tar)
191
- self._add_configs(tar)
192
- for path, arcname in self.path_tuples():
193
- tar.add(path, arcname=arcname, recursive=False, filter=no_mtime)
194
-
195
- blob = bytearray(buf.getvalue())
196
- blob[4:8] = [0] * 4 # Reset 4 bytes from offset 4 to account for ts
197
- return blob
198
-
199
- def __str__(self):
200
- return "<code package for flow %s (created @ %s)>" % (
201
- self.flow_name,
202
- time.strftime("%a, %d %b %Y %H:%M:%S", self.create_time),
203
- )