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.
Files changed (34) hide show
  1. metaflow/_vendor/imghdr/__init__.py +180 -0
  2. metaflow/cmd/develop/stub_generator.py +19 -2
  3. metaflow/plugins/__init__.py +3 -0
  4. metaflow/plugins/airflow/airflow.py +6 -0
  5. metaflow/plugins/argo/argo_workflows.py +316 -287
  6. metaflow/plugins/argo/exit_hooks.py +209 -0
  7. metaflow/plugins/aws/aws_utils.py +1 -1
  8. metaflow/plugins/aws/step_functions/step_functions.py +6 -0
  9. metaflow/plugins/cards/card_cli.py +20 -1
  10. metaflow/plugins/cards/card_creator.py +24 -1
  11. metaflow/plugins/cards/card_decorator.py +57 -1
  12. metaflow/plugins/cards/card_modules/convert_to_native_type.py +5 -2
  13. metaflow/plugins/cards/card_modules/test_cards.py +16 -0
  14. metaflow/plugins/cards/metadata.py +22 -0
  15. metaflow/plugins/exit_hook/__init__.py +0 -0
  16. metaflow/plugins/exit_hook/exit_hook_decorator.py +46 -0
  17. metaflow/plugins/exit_hook/exit_hook_script.py +52 -0
  18. metaflow/plugins/secrets/__init__.py +3 -0
  19. metaflow/plugins/secrets/secrets_decorator.py +9 -173
  20. metaflow/plugins/secrets/secrets_func.py +49 -0
  21. metaflow/plugins/secrets/secrets_spec.py +101 -0
  22. metaflow/plugins/secrets/utils.py +74 -0
  23. metaflow/runner/metaflow_runner.py +16 -1
  24. metaflow/runtime.py +45 -0
  25. metaflow/version.py +1 -1
  26. {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/Tiltfile +27 -2
  27. {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/METADATA +2 -2
  28. {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/RECORD +34 -25
  29. {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/Makefile +0 -0
  30. {metaflow-2.15.18.data → metaflow-2.15.20.data}/data/share/metaflow/devtools/pick_services.sh +0 -0
  31. {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/WHEEL +0 -0
  32. {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/entry_points.txt +0 -0
  33. {metaflow-2.15.18.dist-info → metaflow-2.15.20.dist-info}/licenses/LICENSE +0 -0
  34. {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")
@@ -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(