ob-metaflow 2.15.18.1__py2.py3-none-any.whl → 2.16.0.1__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ob-metaflow might be problematic. Click here for more details.
- metaflow/__init__.py +7 -1
- metaflow/_vendor/imghdr/__init__.py +180 -0
- metaflow/cli.py +16 -1
- metaflow/cli_components/init_cmd.py +1 -0
- metaflow/cli_components/run_cmds.py +6 -2
- metaflow/client/core.py +22 -30
- metaflow/cmd/develop/stub_generator.py +19 -2
- metaflow/datastore/task_datastore.py +0 -1
- metaflow/debug.py +5 -0
- metaflow/decorators.py +230 -70
- metaflow/extension_support/__init__.py +15 -8
- metaflow/extension_support/_empty_file.py +2 -2
- metaflow/flowspec.py +80 -53
- metaflow/graph.py +24 -2
- metaflow/meta_files.py +13 -0
- metaflow/metadata_provider/metadata.py +7 -1
- metaflow/metaflow_config.py +5 -0
- metaflow/metaflow_environment.py +82 -25
- metaflow/metaflow_version.py +1 -1
- metaflow/package/__init__.py +664 -0
- metaflow/packaging_sys/__init__.py +870 -0
- metaflow/packaging_sys/backend.py +113 -0
- metaflow/packaging_sys/distribution_support.py +153 -0
- metaflow/packaging_sys/tar_backend.py +86 -0
- metaflow/packaging_sys/utils.py +91 -0
- metaflow/packaging_sys/v1.py +476 -0
- metaflow/plugins/__init__.py +3 -0
- metaflow/plugins/airflow/airflow.py +11 -1
- metaflow/plugins/airflow/airflow_cli.py +15 -4
- metaflow/plugins/argo/argo_workflows.py +346 -301
- metaflow/plugins/argo/argo_workflows_cli.py +16 -4
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/aws/aws_utils.py +1 -1
- metaflow/plugins/aws/batch/batch.py +22 -3
- metaflow/plugins/aws/batch/batch_cli.py +3 -0
- metaflow/plugins/aws/batch/batch_decorator.py +13 -5
- metaflow/plugins/aws/step_functions/step_functions.py +10 -1
- metaflow/plugins/aws/step_functions/step_functions_cli.py +15 -4
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_decorator.py +57 -6
- metaflow/plugins/cards/card_modules/convert_to_native_type.py +5 -2
- metaflow/plugins/cards/card_modules/test_cards.py +16 -0
- metaflow/plugins/cards/metadata.py +22 -0
- metaflow/plugins/exit_hook/__init__.py +0 -0
- metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
- metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
- metaflow/plugins/kubernetes/kubernetes.py +8 -1
- metaflow/plugins/kubernetes/kubernetes_cli.py +3 -0
- metaflow/plugins/kubernetes/kubernetes_decorator.py +13 -5
- metaflow/plugins/package_cli.py +25 -23
- metaflow/plugins/parallel_decorator.py +4 -2
- metaflow/plugins/pypi/bootstrap.py +8 -2
- metaflow/plugins/pypi/conda_decorator.py +39 -82
- metaflow/plugins/pypi/conda_environment.py +6 -2
- metaflow/plugins/pypi/pypi_decorator.py +4 -4
- metaflow/plugins/secrets/__init__.py +3 -0
- metaflow/plugins/secrets/secrets_decorator.py +9 -173
- metaflow/plugins/secrets/secrets_func.py +49 -0
- metaflow/plugins/secrets/secrets_spec.py +101 -0
- metaflow/plugins/secrets/utils.py +74 -0
- metaflow/plugins/test_unbounded_foreach_decorator.py +2 -2
- metaflow/plugins/timeout_decorator.py +0 -1
- metaflow/plugins/uv/bootstrap.py +11 -0
- metaflow/plugins/uv/uv_environment.py +4 -2
- metaflow/pylint_wrapper.py +5 -1
- metaflow/runner/click_api.py +5 -4
- metaflow/runner/metaflow_runner.py +16 -1
- metaflow/runner/subprocess_manager.py +14 -2
- metaflow/runtime.py +82 -11
- metaflow/task.py +91 -7
- metaflow/user_configs/config_options.py +13 -8
- metaflow/user_configs/config_parameters.py +0 -4
- metaflow/user_decorators/__init__.py +0 -0
- metaflow/user_decorators/common.py +144 -0
- metaflow/user_decorators/mutable_flow.py +499 -0
- metaflow/user_decorators/mutable_step.py +424 -0
- metaflow/user_decorators/user_flow_decorator.py +263 -0
- metaflow/user_decorators/user_step_decorator.py +712 -0
- metaflow/util.py +4 -1
- metaflow/version.py +1 -1
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/Tiltfile +27 -2
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/METADATA +2 -2
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/RECORD +90 -70
- metaflow/info_file.py +0 -25
- metaflow/package.py +0 -203
- metaflow/user_configs/config_decorators.py +0 -568
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/Makefile +0 -0
- {ob_metaflow-2.15.18.1.data → ob_metaflow-2.16.0.1.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/WHEEL +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/entry_points.txt +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/licenses/LICENSE +0 -0
- {ob_metaflow-2.15.18.1.dist-info → ob_metaflow-2.16.0.1.dist-info}/top_level.txt +0 -0
metaflow/__init__.py
CHANGED
|
@@ -104,7 +104,13 @@ from .flowspec import FlowSpec
|
|
|
104
104
|
from .parameters import Parameter, JSONTypeClass, JSONType
|
|
105
105
|
|
|
106
106
|
from .user_configs.config_parameters import Config, ConfigValue, config_expr
|
|
107
|
-
from .
|
|
107
|
+
from .user_decorators.user_step_decorator import (
|
|
108
|
+
UserStepDecorator,
|
|
109
|
+
StepMutator,
|
|
110
|
+
user_step_decorator,
|
|
111
|
+
USER_SKIP_STEP,
|
|
112
|
+
)
|
|
113
|
+
from .user_decorators.user_flow_decorator import FlowMutator
|
|
108
114
|
|
|
109
115
|
# data layer
|
|
110
116
|
# For historical reasons, we make metaflow.plugins.datatools accessible as
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Recognize image file formats based on their first few bytes."""
|
|
2
|
+
|
|
3
|
+
from os import PathLike
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
__all__ = ["what"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
warnings._deprecated(__name__, remove=(3, 13))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
#-------------------------#
|
|
13
|
+
# Recognize image headers #
|
|
14
|
+
#-------------------------#
|
|
15
|
+
|
|
16
|
+
def what(file, h=None):
|
|
17
|
+
"""Return the type of image contained in a file or byte stream."""
|
|
18
|
+
f = None
|
|
19
|
+
try:
|
|
20
|
+
if h is None:
|
|
21
|
+
if isinstance(file, (str, PathLike)):
|
|
22
|
+
f = open(file, 'rb')
|
|
23
|
+
h = f.read(32)
|
|
24
|
+
else:
|
|
25
|
+
location = file.tell()
|
|
26
|
+
h = file.read(32)
|
|
27
|
+
file.seek(location)
|
|
28
|
+
for tf in tests:
|
|
29
|
+
res = tf(h, f)
|
|
30
|
+
if res:
|
|
31
|
+
return res
|
|
32
|
+
finally:
|
|
33
|
+
if f: f.close()
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#---------------------------------#
|
|
38
|
+
# Subroutines per image file type #
|
|
39
|
+
#---------------------------------#
|
|
40
|
+
|
|
41
|
+
tests = []
|
|
42
|
+
|
|
43
|
+
def test_jpeg(h, f):
|
|
44
|
+
"""Test for JPEG data with JFIF or Exif markers; and raw JPEG."""
|
|
45
|
+
if h[6:10] in (b'JFIF', b'Exif'):
|
|
46
|
+
return 'jpeg'
|
|
47
|
+
elif h[:4] == b'\xff\xd8\xff\xdb':
|
|
48
|
+
return 'jpeg'
|
|
49
|
+
|
|
50
|
+
tests.append(test_jpeg)
|
|
51
|
+
|
|
52
|
+
def test_png(h, f):
|
|
53
|
+
"""Verify if the image is a PNG."""
|
|
54
|
+
if h.startswith(b'\211PNG\r\n\032\n'):
|
|
55
|
+
return 'png'
|
|
56
|
+
|
|
57
|
+
tests.append(test_png)
|
|
58
|
+
|
|
59
|
+
def test_gif(h, f):
|
|
60
|
+
"""Verify if the image is a GIF ('87 or '89 variants)."""
|
|
61
|
+
if h[:6] in (b'GIF87a', b'GIF89a'):
|
|
62
|
+
return 'gif'
|
|
63
|
+
|
|
64
|
+
tests.append(test_gif)
|
|
65
|
+
|
|
66
|
+
def test_tiff(h, f):
|
|
67
|
+
"""Verify if the image is a TIFF (can be in Motorola or Intel byte order)."""
|
|
68
|
+
if h[:2] in (b'MM', b'II'):
|
|
69
|
+
return 'tiff'
|
|
70
|
+
|
|
71
|
+
tests.append(test_tiff)
|
|
72
|
+
|
|
73
|
+
def test_rgb(h, f):
|
|
74
|
+
"""test for the SGI image library."""
|
|
75
|
+
if h.startswith(b'\001\332'):
|
|
76
|
+
return 'rgb'
|
|
77
|
+
|
|
78
|
+
tests.append(test_rgb)
|
|
79
|
+
|
|
80
|
+
def test_pbm(h, f):
|
|
81
|
+
"""Verify if the image is a PBM (portable bitmap)."""
|
|
82
|
+
if len(h) >= 3 and \
|
|
83
|
+
h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
|
|
84
|
+
return 'pbm'
|
|
85
|
+
|
|
86
|
+
tests.append(test_pbm)
|
|
87
|
+
|
|
88
|
+
def test_pgm(h, f):
|
|
89
|
+
"""Verify if the image is a PGM (portable graymap)."""
|
|
90
|
+
if len(h) >= 3 and \
|
|
91
|
+
h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
|
|
92
|
+
return 'pgm'
|
|
93
|
+
|
|
94
|
+
tests.append(test_pgm)
|
|
95
|
+
|
|
96
|
+
def test_ppm(h, f):
|
|
97
|
+
"""Verify if the image is a PPM (portable pixmap)."""
|
|
98
|
+
if len(h) >= 3 and \
|
|
99
|
+
h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
|
|
100
|
+
return 'ppm'
|
|
101
|
+
|
|
102
|
+
tests.append(test_ppm)
|
|
103
|
+
|
|
104
|
+
def test_rast(h, f):
|
|
105
|
+
"""test for the Sun raster file."""
|
|
106
|
+
if h.startswith(b'\x59\xA6\x6A\x95'):
|
|
107
|
+
return 'rast'
|
|
108
|
+
|
|
109
|
+
tests.append(test_rast)
|
|
110
|
+
|
|
111
|
+
def test_xbm(h, f):
|
|
112
|
+
"""Verify if the image is a X bitmap (X10 or X11)."""
|
|
113
|
+
if h.startswith(b'#define '):
|
|
114
|
+
return 'xbm'
|
|
115
|
+
|
|
116
|
+
tests.append(test_xbm)
|
|
117
|
+
|
|
118
|
+
def test_bmp(h, f):
|
|
119
|
+
"""Verify if the image is a BMP file."""
|
|
120
|
+
if h.startswith(b'BM'):
|
|
121
|
+
return 'bmp'
|
|
122
|
+
|
|
123
|
+
tests.append(test_bmp)
|
|
124
|
+
|
|
125
|
+
def test_webp(h, f):
|
|
126
|
+
"""Verify if the image is a WebP."""
|
|
127
|
+
if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
|
|
128
|
+
return 'webp'
|
|
129
|
+
|
|
130
|
+
tests.append(test_webp)
|
|
131
|
+
|
|
132
|
+
def test_exr(h, f):
|
|
133
|
+
"""verify is the image ia a OpenEXR fileOpenEXR."""
|
|
134
|
+
if h.startswith(b'\x76\x2f\x31\x01'):
|
|
135
|
+
return 'exr'
|
|
136
|
+
|
|
137
|
+
tests.append(test_exr)
|
|
138
|
+
|
|
139
|
+
#--------------------#
|
|
140
|
+
# Small test program #
|
|
141
|
+
#--------------------#
|
|
142
|
+
|
|
143
|
+
def test():
|
|
144
|
+
import sys
|
|
145
|
+
recursive = 0
|
|
146
|
+
if sys.argv[1:] and sys.argv[1] == '-r':
|
|
147
|
+
del sys.argv[1:2]
|
|
148
|
+
recursive = 1
|
|
149
|
+
try:
|
|
150
|
+
if sys.argv[1:]:
|
|
151
|
+
testall(sys.argv[1:], recursive, 1)
|
|
152
|
+
else:
|
|
153
|
+
testall(['.'], recursive, 1)
|
|
154
|
+
except KeyboardInterrupt:
|
|
155
|
+
sys.stderr.write('\n[Interrupted]\n')
|
|
156
|
+
sys.exit(1)
|
|
157
|
+
|
|
158
|
+
def testall(list, recursive, toplevel):
|
|
159
|
+
import sys
|
|
160
|
+
import os
|
|
161
|
+
for filename in list:
|
|
162
|
+
if os.path.isdir(filename):
|
|
163
|
+
print(filename + '/:', end=' ')
|
|
164
|
+
if recursive or toplevel:
|
|
165
|
+
print('recursing down:')
|
|
166
|
+
import glob
|
|
167
|
+
names = glob.glob(os.path.join(glob.escape(filename), '*'))
|
|
168
|
+
testall(names, recursive, 0)
|
|
169
|
+
else:
|
|
170
|
+
print('*** directory (use -r) ***')
|
|
171
|
+
else:
|
|
172
|
+
print(filename + ':', end=' ')
|
|
173
|
+
sys.stdout.flush()
|
|
174
|
+
try:
|
|
175
|
+
print(what(filename))
|
|
176
|
+
except OSError:
|
|
177
|
+
print('*** not found ***')
|
|
178
|
+
|
|
179
|
+
if __name__ == '__main__':
|
|
180
|
+
test()
|
metaflow/cli.py
CHANGED
|
@@ -28,6 +28,7 @@ from .metaflow_config import (
|
|
|
28
28
|
from .metaflow_current import current
|
|
29
29
|
from metaflow.system import _system_monitor, _system_logger
|
|
30
30
|
from .metaflow_environment import MetaflowEnvironment
|
|
31
|
+
from .packaging_sys import MetaflowCodeContent
|
|
31
32
|
from .plugins import (
|
|
32
33
|
DATASTORES,
|
|
33
34
|
ENVIRONMENTS,
|
|
@@ -152,8 +153,13 @@ def check(obj, warnings=False):
|
|
|
152
153
|
def show(obj):
|
|
153
154
|
echo_always("\n%s" % obj.graph.doc)
|
|
154
155
|
for node_name in obj.graph.sorted_nodes:
|
|
156
|
+
echo_always("")
|
|
155
157
|
node = obj.graph[node_name]
|
|
156
|
-
|
|
158
|
+
for deco in node.decorators:
|
|
159
|
+
echo_always("@%s" % deco.name, err=False)
|
|
160
|
+
for deco in node.wrappers:
|
|
161
|
+
echo_always("@%s" % deco.decorator_name, err=False)
|
|
162
|
+
echo_always("Step *%s*" % node.name, err=False)
|
|
157
163
|
echo_always(node.doc if node.doc else "?", indent=True, err=False)
|
|
158
164
|
if node.type != "end":
|
|
159
165
|
echo_always(
|
|
@@ -336,6 +342,11 @@ def start(
|
|
|
336
342
|
echo(" executing *%s*" % ctx.obj.flow.name, fg="magenta", nl=False)
|
|
337
343
|
echo(" for *%s*" % resolve_identity(), fg="magenta")
|
|
338
344
|
|
|
345
|
+
# Check if we need to setup the distribution finder (if running )
|
|
346
|
+
dist_info = MetaflowCodeContent.get_distribution_finder()
|
|
347
|
+
if dist_info:
|
|
348
|
+
sys.meta_path.append(dist_info)
|
|
349
|
+
|
|
339
350
|
# Setup the context
|
|
340
351
|
cli_args._set_top_kwargs(ctx.params)
|
|
341
352
|
ctx.obj.echo = echo
|
|
@@ -436,6 +447,10 @@ def start(
|
|
|
436
447
|
# be raised. For resume, since we ignore those options, we ignore the error.
|
|
437
448
|
raise ctx.obj.delayed_config_exception
|
|
438
449
|
|
|
450
|
+
# Init all values in the config decorators and then process them
|
|
451
|
+
for decorator in ctx.obj.flow._flow_state.get(_FlowState.CONFIG_DECORATORS, []):
|
|
452
|
+
decorator.external_init()
|
|
453
|
+
|
|
439
454
|
new_cls = ctx.obj.flow._process_config_decorators(config_options)
|
|
440
455
|
if new_cls:
|
|
441
456
|
ctx.obj.flow = new_cls(use_cli=False)
|
|
@@ -46,6 +46,7 @@ def init(obj, run_id=None, task_id=None, tags=None, **kwargs):
|
|
|
46
46
|
obj.event_logger,
|
|
47
47
|
obj.monitor,
|
|
48
48
|
run_id=run_id,
|
|
49
|
+
skip_decorator_hooks=True,
|
|
49
50
|
)
|
|
50
51
|
obj.flow._set_constants(obj.graph, kwargs, obj.config_options)
|
|
51
52
|
runtime.persist_constants(task_id=task_id)
|
|
@@ -8,7 +8,7 @@ from .. import decorators, namespace, parameters, tracing
|
|
|
8
8
|
from ..exception import CommandException
|
|
9
9
|
from ..graph import FlowGraph
|
|
10
10
|
from ..metaflow_current import current
|
|
11
|
-
from ..metaflow_config import DEFAULT_DECOSPECS
|
|
11
|
+
from ..metaflow_config import DEFAULT_DECOSPECS, FEAT_ALWAYS_UPLOAD_CODE_PACKAGE
|
|
12
12
|
from ..package import MetaflowPackage
|
|
13
13
|
from ..runtime import NativeRuntime
|
|
14
14
|
from ..system import _system_logger
|
|
@@ -61,7 +61,11 @@ def before_run(obj, tags, decospecs):
|
|
|
61
61
|
# We explicitly avoid doing this in `start` since it is invoked for every
|
|
62
62
|
# step in the run.
|
|
63
63
|
obj.package = MetaflowPackage(
|
|
64
|
-
obj.flow,
|
|
64
|
+
obj.flow,
|
|
65
|
+
obj.environment,
|
|
66
|
+
obj.echo,
|
|
67
|
+
suffixes=obj.package_suffixes,
|
|
68
|
+
flow_datastore=obj.flow_datastore if FEAT_ALWAYS_UPLOAD_CODE_PACKAGE else None,
|
|
65
69
|
)
|
|
66
70
|
|
|
67
71
|
|
metaflow/client/core.py
CHANGED
|
@@ -32,11 +32,12 @@ from metaflow.exception import (
|
|
|
32
32
|
from metaflow.includefile import IncludedFile
|
|
33
33
|
from metaflow.metaflow_config import DEFAULT_METADATA, MAX_ATTEMPTS
|
|
34
34
|
from metaflow.metaflow_environment import MetaflowEnvironment
|
|
35
|
+
from metaflow.package import MetaflowPackage
|
|
36
|
+
from metaflow.packaging_sys import ContentType
|
|
35
37
|
from metaflow.plugins import ENVIRONMENTS, METADATA_PROVIDERS
|
|
36
38
|
from metaflow.unbounded_foreach import CONTROL_TASK_TAG
|
|
37
39
|
from metaflow.util import cached_property, is_stringish, resolve_identity, to_unicode
|
|
38
40
|
|
|
39
|
-
from ..info_file import INFO_FILE
|
|
40
41
|
from .filecache import FileCache
|
|
41
42
|
|
|
42
43
|
if TYPE_CHECKING:
|
|
@@ -816,20 +817,26 @@ class MetaflowCode(object):
|
|
|
816
817
|
self._path = info["location"]
|
|
817
818
|
self._ds_type = info["ds_type"]
|
|
818
819
|
self._sha = info["sha"]
|
|
820
|
+
self._code_metadata = info.get(
|
|
821
|
+
"metadata",
|
|
822
|
+
'{"version": 0, "archive_format": "tgz", "mfcontent_version": 0}',
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
self._backend = MetaflowPackage.get_backend(self._code_metadata)
|
|
819
826
|
|
|
820
827
|
if filecache is None:
|
|
821
828
|
filecache = FileCache()
|
|
822
829
|
_, blobdata = filecache.get_data(
|
|
823
830
|
self._ds_type, self._flow_name, self._path, self._sha
|
|
824
831
|
)
|
|
825
|
-
|
|
826
|
-
self.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
832
|
+
self._code_obj = BytesIO(blobdata)
|
|
833
|
+
self._info = MetaflowPackage.cls_get_info(self._code_metadata, self._code_obj)
|
|
834
|
+
if self._info:
|
|
835
|
+
self._flowspec = MetaflowPackage.cls_get_content(
|
|
836
|
+
self._code_metadata, self._code_obj, self._info["script"]
|
|
837
|
+
)
|
|
838
|
+
else:
|
|
839
|
+
raise MetaflowInternalError("Code package metadata is invalid.")
|
|
833
840
|
|
|
834
841
|
@property
|
|
835
842
|
def path(self) -> str:
|
|
@@ -877,7 +884,9 @@ class MetaflowCode(object):
|
|
|
877
884
|
TarFile
|
|
878
885
|
TarFile for everything in this code package
|
|
879
886
|
"""
|
|
880
|
-
|
|
887
|
+
if self._backend.type == "tgz":
|
|
888
|
+
return self._backend.cls_open(self._code_obj)
|
|
889
|
+
raise RuntimeError("Archive is not a tarball")
|
|
881
890
|
|
|
882
891
|
def extract(self) -> TemporaryDirectory:
|
|
883
892
|
"""
|
|
@@ -908,27 +917,10 @@ class MetaflowCode(object):
|
|
|
908
917
|
The directory and its contents are automatically deleted when
|
|
909
918
|
this object is garbage collected.
|
|
910
919
|
"""
|
|
911
|
-
exclusions = [
|
|
912
|
-
"metaflow/",
|
|
913
|
-
"metaflow_extensions/",
|
|
914
|
-
"INFO",
|
|
915
|
-
"CONFIG_PARAMETERS",
|
|
916
|
-
"conda.manifest",
|
|
917
|
-
# This file is created when using the conda/pypi features available in
|
|
918
|
-
# nflx-metaflow-extensions: https://github.com/Netflix/metaflow-nflx-extensions
|
|
919
|
-
"condav2-1.cnd",
|
|
920
|
-
]
|
|
921
|
-
members = [
|
|
922
|
-
m
|
|
923
|
-
for m in self.tarball.getmembers()
|
|
924
|
-
if not any(
|
|
925
|
-
(x.endswith("/") and m.name.startswith(x)) or (m.name == x)
|
|
926
|
-
for x in exclusions
|
|
927
|
-
)
|
|
928
|
-
]
|
|
929
|
-
|
|
930
920
|
tmp = TemporaryDirectory()
|
|
931
|
-
|
|
921
|
+
MetaflowPackage.cls_extract_into(
|
|
922
|
+
self._code_metadata, self._code_obj, tmp.name, ContentType.USER_CONTENT
|
|
923
|
+
)
|
|
932
924
|
return tmp
|
|
933
925
|
|
|
934
926
|
@property
|
|
@@ -7,7 +7,6 @@ import pathlib
|
|
|
7
7
|
import re
|
|
8
8
|
import time
|
|
9
9
|
import typing
|
|
10
|
-
|
|
11
10
|
from datetime import datetime
|
|
12
11
|
from io import StringIO
|
|
13
12
|
from types import ModuleType
|
|
@@ -335,6 +334,8 @@ class StubGenerator:
|
|
|
335
334
|
|
|
336
335
|
# Imports that are needed at the top of the file
|
|
337
336
|
self._imports = set() # type: Set[str]
|
|
337
|
+
|
|
338
|
+
self._sub_module_imports = set() # type: Set[Tuple[str, str]]``
|
|
338
339
|
# Typing imports (behind if TYPE_CHECKING) that are needed at the top of the file
|
|
339
340
|
self._typing_imports = set() # type: Set[str]
|
|
340
341
|
# Typevars that are defined
|
|
@@ -643,6 +644,21 @@ class StubGenerator:
|
|
|
643
644
|
"deployer"
|
|
644
645
|
] = (self._current_module_name + "." + name)
|
|
645
646
|
|
|
647
|
+
# Handle TypedDict gracefully for Python 3.7 compatibility
|
|
648
|
+
# _TypedDictMeta is not available in Python 3.7
|
|
649
|
+
typed_dict_meta = getattr(typing, "_TypedDictMeta", None)
|
|
650
|
+
if typed_dict_meta is not None and isinstance(clazz, typed_dict_meta):
|
|
651
|
+
self._sub_module_imports.add(("typing", "TypedDict"))
|
|
652
|
+
total_flag = getattr(clazz, "__total__", False)
|
|
653
|
+
buff = StringIO()
|
|
654
|
+
# Emit the TypedDict base and total flag
|
|
655
|
+
buff.write(f"class {name}(TypedDict, total={total_flag}):\n")
|
|
656
|
+
# Write out each field from __annotations__
|
|
657
|
+
for field_name, field_type in clazz.__annotations__.items():
|
|
658
|
+
ann = self._get_element_name_with_module(field_type)
|
|
659
|
+
buff.write(f"{TAB}{field_name}: {ann}\n")
|
|
660
|
+
return buff.getvalue()
|
|
661
|
+
|
|
646
662
|
buff = StringIO()
|
|
647
663
|
# Class prototype
|
|
648
664
|
buff.write("class " + name.split(".")[-1] + "(")
|
|
@@ -987,7 +1003,6 @@ class StubGenerator:
|
|
|
987
1003
|
]
|
|
988
1004
|
|
|
989
1005
|
docs = split_docs(raw_doc, section_boundaries)
|
|
990
|
-
|
|
991
1006
|
parameters, no_arg_version = parse_params_from_doc(docs["param_doc"])
|
|
992
1007
|
|
|
993
1008
|
if docs["add_to_current_doc"]:
|
|
@@ -1515,6 +1530,8 @@ class StubGenerator:
|
|
|
1515
1530
|
f.write("import " + module + "\n")
|
|
1516
1531
|
if module == "typing":
|
|
1517
1532
|
imported_typing = True
|
|
1533
|
+
for module, sub_module in self._sub_module_imports:
|
|
1534
|
+
f.write(f"from {module} import {sub_module}\n")
|
|
1518
1535
|
if self._typing_imports:
|
|
1519
1536
|
if not imported_typing:
|
|
1520
1537
|
f.write("import typing\n")
|
metaflow/debug.py
CHANGED
|
@@ -42,6 +42,11 @@ class Debug(object):
|
|
|
42
42
|
filename = inspect.stack()[1][1]
|
|
43
43
|
print("debug[%s %s:%s]: %s" % (typ, filename, lineno, s), file=sys.stderr)
|
|
44
44
|
|
|
45
|
+
def __getattr__(self, name):
|
|
46
|
+
# Small piece of code to get pyright and other linters to recognize that there
|
|
47
|
+
# are dynamic attributes.
|
|
48
|
+
return getattr(self, name)
|
|
49
|
+
|
|
45
50
|
def noop(self, args):
|
|
46
51
|
pass
|
|
47
52
|
|