metaflow 2.15.18__py2.py3-none-any.whl → 2.15.20__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.
- metaflow/_vendor/imghdr/__init__.py +180 -0
- metaflow/cmd/develop/stub_generator.py +19 -2
- metaflow/plugins/__init__.py +3 -0
- metaflow/plugins/airflow/airflow.py +6 -0
- metaflow/plugins/argo/argo_workflows.py +316 -287
- metaflow/plugins/argo/exit_hooks.py +209 -0
- metaflow/plugins/aws/aws_utils.py +1 -1
- metaflow/plugins/aws/step_functions/step_functions.py +6 -0
- metaflow/plugins/cards/card_cli.py +20 -1
- metaflow/plugins/cards/card_creator.py +24 -1
- metaflow/plugins/cards/card_decorator.py +57 -1
- 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/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/runner/metaflow_runner.py +16 -1
- metaflow/runtime.py +45 -0
- metaflow/version.py +1 -1
- {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/Tiltfile +27 -2
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/METADATA +2 -2
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/RECORD +34 -25
- {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/Makefile +0 -0
- {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/WHEEL +0 -0
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/entry_points.txt +0 -0
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/licenses/LICENSE +0 -0
- {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/top_level.txt +0 -0
@@ -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()
|
@@ -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/plugins/__init__.py
CHANGED
@@ -69,6 +69,7 @@ FLOW_DECORATORS_DESC = [
|
|
69
69
|
("trigger_on_finish", ".events_decorator.TriggerOnFinishDecorator"),
|
70
70
|
("pypi_base", ".pypi.pypi_decorator.PyPIFlowDecorator"),
|
71
71
|
("conda_base", ".pypi.conda_decorator.CondaFlowDecorator"),
|
72
|
+
("exit_hook", ".exit_hook.exit_hook_decorator.ExitHookDecorator"),
|
72
73
|
]
|
73
74
|
|
74
75
|
# Add environments here
|
@@ -240,6 +241,7 @@ from .cards.card_modules.test_cards import (
|
|
240
241
|
TestTimeoutCard,
|
241
242
|
TestRefreshCard,
|
242
243
|
TestRefreshComponentCard,
|
244
|
+
TestImageCard,
|
243
245
|
)
|
244
246
|
|
245
247
|
CARDS = [
|
@@ -258,6 +260,7 @@ CARDS = [
|
|
258
260
|
DefaultCardJSON,
|
259
261
|
TestRefreshCard,
|
260
262
|
TestRefreshComponentCard,
|
263
|
+
TestImageCard,
|
261
264
|
]
|
262
265
|
merge_lists(CARDS, MF_EXTERNAL_CARDS, "type")
|
263
266
|
|
@@ -654,6 +654,12 @@ class Airflow(object):
|
|
654
654
|
"to Airflow is not supported currently."
|
655
655
|
)
|
656
656
|
|
657
|
+
if self.flow._flow_decorators.get("exit_hook"):
|
658
|
+
raise AirflowException(
|
659
|
+
"Deploying flows with the @exit_hook decorator "
|
660
|
+
"to Airflow is not currently supported."
|
661
|
+
)
|
662
|
+
|
657
663
|
# Visit every node of the flow and recursively build the state machine.
|
658
664
|
def _visit(node, workflow, exit_node=None):
|
659
665
|
kube_deco = dict(
|