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
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 .user_configs.config_decorators import CustomFlowDecorator, CustomStepDecorator
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
@@ -128,8 +134,8 @@ _import_tl_plugins(globals())
128
134
  # this auto-generates decorator functions from Decorator objects
129
135
  # in the top-level metaflow namespace
130
136
  _import_plugin_decorators(globals())
131
- # Setting card import for only python 3.4
132
- if sys.version_info[0] >= 3 and sys.version_info[1] >= 4:
137
+ # Setting card import for only python 3.6
138
+ if sys.version_info[0] >= 3 and sys.version_info[1] >= 6:
133
139
  from . import cards
134
140
 
135
141
  # Client
@@ -140,6 +146,7 @@ from .client import (
140
146
  metadata,
141
147
  get_metadata,
142
148
  default_metadata,
149
+ inspect_spin,
143
150
  Metaflow,
144
151
  Flow,
145
152
  Run,
@@ -0,0 +1,186 @@
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
+ # python-deadlib: Replace deprecation warning not to raise exception
10
+ warnings.warn(
11
+ f"{__name__} was removed in Python 3.13. "
12
+ f"Please be aware that you are currently NOT using standard '{__name__}', "
13
+ f"but instead a separately installed 'standard-{__name__}'.",
14
+ DeprecationWarning, stacklevel=2
15
+ )
16
+
17
+
18
+ #-------------------------#
19
+ # Recognize image headers #
20
+ #-------------------------#
21
+
22
+ def what(file, h=None):
23
+ """Return the type of image contained in a file or byte stream."""
24
+ f = None
25
+ try:
26
+ if h is None:
27
+ if isinstance(file, (str, PathLike)):
28
+ f = open(file, 'rb')
29
+ h = f.read(32)
30
+ else:
31
+ location = file.tell()
32
+ h = file.read(32)
33
+ file.seek(location)
34
+ for tf in tests:
35
+ res = tf(h, f)
36
+ if res:
37
+ return res
38
+ finally:
39
+ if f: f.close()
40
+ return None
41
+
42
+
43
+ #---------------------------------#
44
+ # Subroutines per image file type #
45
+ #---------------------------------#
46
+
47
+ tests = []
48
+
49
+ def test_jpeg(h, f):
50
+ """Test for JPEG data with JFIF or Exif markers; and raw JPEG."""
51
+ if h[6:10] in (b'JFIF', b'Exif'):
52
+ return 'jpeg'
53
+ elif h[:4] == b'\xff\xd8\xff\xdb':
54
+ return 'jpeg'
55
+
56
+ tests.append(test_jpeg)
57
+
58
+ def test_png(h, f):
59
+ """Verify if the image is a PNG."""
60
+ if h.startswith(b'\211PNG\r\n\032\n'):
61
+ return 'png'
62
+
63
+ tests.append(test_png)
64
+
65
+ def test_gif(h, f):
66
+ """Verify if the image is a GIF ('87 or '89 variants)."""
67
+ if h[:6] in (b'GIF87a', b'GIF89a'):
68
+ return 'gif'
69
+
70
+ tests.append(test_gif)
71
+
72
+ def test_tiff(h, f):
73
+ """Verify if the image is a TIFF (can be in Motorola or Intel byte order)."""
74
+ if h[:2] in (b'MM', b'II'):
75
+ return 'tiff'
76
+
77
+ tests.append(test_tiff)
78
+
79
+ def test_rgb(h, f):
80
+ """test for the SGI image library."""
81
+ if h.startswith(b'\001\332'):
82
+ return 'rgb'
83
+
84
+ tests.append(test_rgb)
85
+
86
+ def test_pbm(h, f):
87
+ """Verify if the image is a PBM (portable bitmap)."""
88
+ if len(h) >= 3 and \
89
+ h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r':
90
+ return 'pbm'
91
+
92
+ tests.append(test_pbm)
93
+
94
+ def test_pgm(h, f):
95
+ """Verify if the image is a PGM (portable graymap)."""
96
+ if len(h) >= 3 and \
97
+ h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r':
98
+ return 'pgm'
99
+
100
+ tests.append(test_pgm)
101
+
102
+ def test_ppm(h, f):
103
+ """Verify if the image is a PPM (portable pixmap)."""
104
+ if len(h) >= 3 and \
105
+ h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r':
106
+ return 'ppm'
107
+
108
+ tests.append(test_ppm)
109
+
110
+ def test_rast(h, f):
111
+ """test for the Sun raster file."""
112
+ if h.startswith(b'\x59\xA6\x6A\x95'):
113
+ return 'rast'
114
+
115
+ tests.append(test_rast)
116
+
117
+ def test_xbm(h, f):
118
+ """Verify if the image is a X bitmap (X10 or X11)."""
119
+ if h.startswith(b'#define '):
120
+ return 'xbm'
121
+
122
+ tests.append(test_xbm)
123
+
124
+ def test_bmp(h, f):
125
+ """Verify if the image is a BMP file."""
126
+ if h.startswith(b'BM'):
127
+ return 'bmp'
128
+
129
+ tests.append(test_bmp)
130
+
131
+ def test_webp(h, f):
132
+ """Verify if the image is a WebP."""
133
+ if h.startswith(b'RIFF') and h[8:12] == b'WEBP':
134
+ return 'webp'
135
+
136
+ tests.append(test_webp)
137
+
138
+ def test_exr(h, f):
139
+ """verify is the image ia a OpenEXR fileOpenEXR."""
140
+ if h.startswith(b'\x76\x2f\x31\x01'):
141
+ return 'exr'
142
+
143
+ tests.append(test_exr)
144
+
145
+ #--------------------#
146
+ # Small test program #
147
+ #--------------------#
148
+
149
+ def test():
150
+ import sys
151
+ recursive = 0
152
+ if sys.argv[1:] and sys.argv[1] == '-r':
153
+ del sys.argv[1:2]
154
+ recursive = 1
155
+ try:
156
+ if sys.argv[1:]:
157
+ testall(sys.argv[1:], recursive, 1)
158
+ else:
159
+ testall(['.'], recursive, 1)
160
+ except KeyboardInterrupt:
161
+ sys.stderr.write('\n[Interrupted]\n')
162
+ sys.exit(1)
163
+
164
+ def testall(list, recursive, toplevel):
165
+ import sys
166
+ import os
167
+ for filename in list:
168
+ if os.path.isdir(filename):
169
+ print(filename + '/:', end=' ')
170
+ if recursive or toplevel:
171
+ print('recursing down:')
172
+ import glob
173
+ names = glob.glob(os.path.join(glob.escape(filename), '*'))
174
+ testall(names, recursive, 0)
175
+ else:
176
+ print('*** directory (use -r) ***')
177
+ else:
178
+ print(filename + ':', end=' ')
179
+ sys.stdout.flush()
180
+ try:
181
+ print(what(filename))
182
+ except OSError:
183
+ print('*** not found ***')
184
+
185
+ if __name__ == '__main__':
186
+ test()
@@ -0,0 +1,427 @@
1
+
2
+ from .error import *
3
+
4
+ from .tokens import *
5
+ from .events import *
6
+ from .nodes import *
7
+
8
+ from .loader import *
9
+ from .dumper import *
10
+
11
+ __version__ = '5.3.1'
12
+ try:
13
+ from .cyaml import *
14
+ __with_libyaml__ = True
15
+ except ImportError:
16
+ __with_libyaml__ = False
17
+
18
+ import io
19
+
20
+ #------------------------------------------------------------------------------
21
+ # Warnings control
22
+ #------------------------------------------------------------------------------
23
+
24
+ # 'Global' warnings state:
25
+ _warnings_enabled = {
26
+ 'YAMLLoadWarning': True,
27
+ }
28
+
29
+ # Get or set global warnings' state
30
+ def warnings(settings=None):
31
+ if settings is None:
32
+ return _warnings_enabled
33
+
34
+ if type(settings) is dict:
35
+ for key in settings:
36
+ if key in _warnings_enabled:
37
+ _warnings_enabled[key] = settings[key]
38
+
39
+ # Warn when load() is called without Loader=...
40
+ class YAMLLoadWarning(RuntimeWarning):
41
+ pass
42
+
43
+ def load_warning(method):
44
+ if _warnings_enabled['YAMLLoadWarning'] is False:
45
+ return
46
+
47
+ import warnings
48
+
49
+ message = (
50
+ "calling yaml.%s() without Loader=... is deprecated, as the "
51
+ "default Loader is unsafe. Please read "
52
+ "https://msg.pyyaml.org/load for full details."
53
+ ) % method
54
+
55
+ warnings.warn(message, YAMLLoadWarning, stacklevel=3)
56
+
57
+ #------------------------------------------------------------------------------
58
+ def scan(stream, Loader=Loader):
59
+ """
60
+ Scan a YAML stream and produce scanning tokens.
61
+ """
62
+ loader = Loader(stream)
63
+ try:
64
+ while loader.check_token():
65
+ yield loader.get_token()
66
+ finally:
67
+ loader.dispose()
68
+
69
+ def parse(stream, Loader=Loader):
70
+ """
71
+ Parse a YAML stream and produce parsing events.
72
+ """
73
+ loader = Loader(stream)
74
+ try:
75
+ while loader.check_event():
76
+ yield loader.get_event()
77
+ finally:
78
+ loader.dispose()
79
+
80
+ def compose(stream, Loader=Loader):
81
+ """
82
+ Parse the first YAML document in a stream
83
+ and produce the corresponding representation tree.
84
+ """
85
+ loader = Loader(stream)
86
+ try:
87
+ return loader.get_single_node()
88
+ finally:
89
+ loader.dispose()
90
+
91
+ def compose_all(stream, Loader=Loader):
92
+ """
93
+ Parse all YAML documents in a stream
94
+ and produce corresponding representation trees.
95
+ """
96
+ loader = Loader(stream)
97
+ try:
98
+ while loader.check_node():
99
+ yield loader.get_node()
100
+ finally:
101
+ loader.dispose()
102
+
103
+ def load(stream, Loader=None):
104
+ """
105
+ Parse the first YAML document in a stream
106
+ and produce the corresponding Python object.
107
+ """
108
+ if Loader is None:
109
+ load_warning('load')
110
+ Loader = FullLoader
111
+
112
+ loader = Loader(stream)
113
+ try:
114
+ return loader.get_single_data()
115
+ finally:
116
+ loader.dispose()
117
+
118
+ def load_all(stream, Loader=None):
119
+ """
120
+ Parse all YAML documents in a stream
121
+ and produce corresponding Python objects.
122
+ """
123
+ if Loader is None:
124
+ load_warning('load_all')
125
+ Loader = FullLoader
126
+
127
+ loader = Loader(stream)
128
+ try:
129
+ while loader.check_data():
130
+ yield loader.get_data()
131
+ finally:
132
+ loader.dispose()
133
+
134
+ def full_load(stream):
135
+ """
136
+ Parse the first YAML document in a stream
137
+ and produce the corresponding Python object.
138
+
139
+ Resolve all tags except those known to be
140
+ unsafe on untrusted input.
141
+ """
142
+ return load(stream, FullLoader)
143
+
144
+ def full_load_all(stream):
145
+ """
146
+ Parse all YAML documents in a stream
147
+ and produce corresponding Python objects.
148
+
149
+ Resolve all tags except those known to be
150
+ unsafe on untrusted input.
151
+ """
152
+ return load_all(stream, FullLoader)
153
+
154
+ def safe_load(stream):
155
+ """
156
+ Parse the first YAML document in a stream
157
+ and produce the corresponding Python object.
158
+
159
+ Resolve only basic YAML tags. This is known
160
+ to be safe for untrusted input.
161
+ """
162
+ return load(stream, SafeLoader)
163
+
164
+ def safe_load_all(stream):
165
+ """
166
+ Parse all YAML documents in a stream
167
+ and produce corresponding Python objects.
168
+
169
+ Resolve only basic YAML tags. This is known
170
+ to be safe for untrusted input.
171
+ """
172
+ return load_all(stream, SafeLoader)
173
+
174
+ def unsafe_load(stream):
175
+ """
176
+ Parse the first YAML document in a stream
177
+ and produce the corresponding Python object.
178
+
179
+ Resolve all tags, even those known to be
180
+ unsafe on untrusted input.
181
+ """
182
+ return load(stream, UnsafeLoader)
183
+
184
+ def unsafe_load_all(stream):
185
+ """
186
+ Parse all YAML documents in a stream
187
+ and produce corresponding Python objects.
188
+
189
+ Resolve all tags, even those known to be
190
+ unsafe on untrusted input.
191
+ """
192
+ return load_all(stream, UnsafeLoader)
193
+
194
+ def emit(events, stream=None, Dumper=Dumper,
195
+ canonical=None, indent=None, width=None,
196
+ allow_unicode=None, line_break=None):
197
+ """
198
+ Emit YAML parsing events into a stream.
199
+ If stream is None, return the produced string instead.
200
+ """
201
+ getvalue = None
202
+ if stream is None:
203
+ stream = io.StringIO()
204
+ getvalue = stream.getvalue
205
+ dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
206
+ allow_unicode=allow_unicode, line_break=line_break)
207
+ try:
208
+ for event in events:
209
+ dumper.emit(event)
210
+ finally:
211
+ dumper.dispose()
212
+ if getvalue:
213
+ return getvalue()
214
+
215
+ def serialize_all(nodes, stream=None, Dumper=Dumper,
216
+ canonical=None, indent=None, width=None,
217
+ allow_unicode=None, line_break=None,
218
+ encoding=None, explicit_start=None, explicit_end=None,
219
+ version=None, tags=None):
220
+ """
221
+ Serialize a sequence of representation trees into a YAML stream.
222
+ If stream is None, return the produced string instead.
223
+ """
224
+ getvalue = None
225
+ if stream is None:
226
+ if encoding is None:
227
+ stream = io.StringIO()
228
+ else:
229
+ stream = io.BytesIO()
230
+ getvalue = stream.getvalue
231
+ dumper = Dumper(stream, canonical=canonical, indent=indent, width=width,
232
+ allow_unicode=allow_unicode, line_break=line_break,
233
+ encoding=encoding, version=version, tags=tags,
234
+ explicit_start=explicit_start, explicit_end=explicit_end)
235
+ try:
236
+ dumper.open()
237
+ for node in nodes:
238
+ dumper.serialize(node)
239
+ dumper.close()
240
+ finally:
241
+ dumper.dispose()
242
+ if getvalue:
243
+ return getvalue()
244
+
245
+ def serialize(node, stream=None, Dumper=Dumper, **kwds):
246
+ """
247
+ Serialize a representation tree into a YAML stream.
248
+ If stream is None, return the produced string instead.
249
+ """
250
+ return serialize_all([node], stream, Dumper=Dumper, **kwds)
251
+
252
+ def dump_all(documents, stream=None, Dumper=Dumper,
253
+ default_style=None, default_flow_style=False,
254
+ canonical=None, indent=None, width=None,
255
+ allow_unicode=None, line_break=None,
256
+ encoding=None, explicit_start=None, explicit_end=None,
257
+ version=None, tags=None, sort_keys=True):
258
+ """
259
+ Serialize a sequence of Python objects into a YAML stream.
260
+ If stream is None, return the produced string instead.
261
+ """
262
+ getvalue = None
263
+ if stream is None:
264
+ if encoding is None:
265
+ stream = io.StringIO()
266
+ else:
267
+ stream = io.BytesIO()
268
+ getvalue = stream.getvalue
269
+ dumper = Dumper(stream, default_style=default_style,
270
+ default_flow_style=default_flow_style,
271
+ canonical=canonical, indent=indent, width=width,
272
+ allow_unicode=allow_unicode, line_break=line_break,
273
+ encoding=encoding, version=version, tags=tags,
274
+ explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys)
275
+ try:
276
+ dumper.open()
277
+ for data in documents:
278
+ dumper.represent(data)
279
+ dumper.close()
280
+ finally:
281
+ dumper.dispose()
282
+ if getvalue:
283
+ return getvalue()
284
+
285
+ def dump(data, stream=None, Dumper=Dumper, **kwds):
286
+ """
287
+ Serialize a Python object into a YAML stream.
288
+ If stream is None, return the produced string instead.
289
+ """
290
+ return dump_all([data], stream, Dumper=Dumper, **kwds)
291
+
292
+ def safe_dump_all(documents, stream=None, **kwds):
293
+ """
294
+ Serialize a sequence of Python objects into a YAML stream.
295
+ Produce only basic YAML tags.
296
+ If stream is None, return the produced string instead.
297
+ """
298
+ return dump_all(documents, stream, Dumper=SafeDumper, **kwds)
299
+
300
+ def safe_dump(data, stream=None, **kwds):
301
+ """
302
+ Serialize a Python object into a YAML stream.
303
+ Produce only basic YAML tags.
304
+ If stream is None, return the produced string instead.
305
+ """
306
+ return dump_all([data], stream, Dumper=SafeDumper, **kwds)
307
+
308
+ def add_implicit_resolver(tag, regexp, first=None,
309
+ Loader=None, Dumper=Dumper):
310
+ """
311
+ Add an implicit scalar detector.
312
+ If an implicit scalar value matches the given regexp,
313
+ the corresponding tag is assigned to the scalar.
314
+ first is a sequence of possible initial characters or None.
315
+ """
316
+ if Loader is None:
317
+ loader.Loader.add_implicit_resolver(tag, regexp, first)
318
+ loader.FullLoader.add_implicit_resolver(tag, regexp, first)
319
+ loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first)
320
+ else:
321
+ Loader.add_implicit_resolver(tag, regexp, first)
322
+ Dumper.add_implicit_resolver(tag, regexp, first)
323
+
324
+ def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper):
325
+ """
326
+ Add a path based resolver for the given tag.
327
+ A path is a list of keys that forms a path
328
+ to a node in the representation tree.
329
+ Keys can be string values, integers, or None.
330
+ """
331
+ if Loader is None:
332
+ loader.Loader.add_path_resolver(tag, path, kind)
333
+ loader.FullLoader.add_path_resolver(tag, path, kind)
334
+ loader.UnsafeLoader.add_path_resolver(tag, path, kind)
335
+ else:
336
+ Loader.add_path_resolver(tag, path, kind)
337
+ Dumper.add_path_resolver(tag, path, kind)
338
+
339
+ def add_constructor(tag, constructor, Loader=None):
340
+ """
341
+ Add a constructor for the given tag.
342
+ Constructor is a function that accepts a Loader instance
343
+ and a node object and produces the corresponding Python object.
344
+ """
345
+ if Loader is None:
346
+ loader.Loader.add_constructor(tag, constructor)
347
+ loader.FullLoader.add_constructor(tag, constructor)
348
+ loader.UnsafeLoader.add_constructor(tag, constructor)
349
+ else:
350
+ Loader.add_constructor(tag, constructor)
351
+
352
+ def add_multi_constructor(tag_prefix, multi_constructor, Loader=None):
353
+ """
354
+ Add a multi-constructor for the given tag prefix.
355
+ Multi-constructor is called for a node if its tag starts with tag_prefix.
356
+ Multi-constructor accepts a Loader instance, a tag suffix,
357
+ and a node object and produces the corresponding Python object.
358
+ """
359
+ if Loader is None:
360
+ loader.Loader.add_multi_constructor(tag_prefix, multi_constructor)
361
+ loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor)
362
+ loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor)
363
+ else:
364
+ Loader.add_multi_constructor(tag_prefix, multi_constructor)
365
+
366
+ def add_representer(data_type, representer, Dumper=Dumper):
367
+ """
368
+ Add a representer for the given type.
369
+ Representer is a function accepting a Dumper instance
370
+ and an instance of the given data type
371
+ and producing the corresponding representation node.
372
+ """
373
+ Dumper.add_representer(data_type, representer)
374
+
375
+ def add_multi_representer(data_type, multi_representer, Dumper=Dumper):
376
+ """
377
+ Add a representer for the given type.
378
+ Multi-representer is a function accepting a Dumper instance
379
+ and an instance of the given data type or subtype
380
+ and producing the corresponding representation node.
381
+ """
382
+ Dumper.add_multi_representer(data_type, multi_representer)
383
+
384
+ class YAMLObjectMetaclass(type):
385
+ """
386
+ The metaclass for YAMLObject.
387
+ """
388
+ def __init__(cls, name, bases, kwds):
389
+ super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
390
+ if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
391
+ if isinstance(cls.yaml_loader, list):
392
+ for loader in cls.yaml_loader:
393
+ loader.add_constructor(cls.yaml_tag, cls.from_yaml)
394
+ else:
395
+ cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
396
+
397
+ cls.yaml_dumper.add_representer(cls, cls.to_yaml)
398
+
399
+ class YAMLObject(metaclass=YAMLObjectMetaclass):
400
+ """
401
+ An object that can dump itself to a YAML stream
402
+ and load itself from a YAML stream.
403
+ """
404
+
405
+ __slots__ = () # no direct instantiation, so allow immutable subclasses
406
+
407
+ yaml_loader = [Loader, FullLoader, UnsafeLoader]
408
+ yaml_dumper = Dumper
409
+
410
+ yaml_tag = None
411
+ yaml_flow_style = None
412
+
413
+ @classmethod
414
+ def from_yaml(cls, loader, node):
415
+ """
416
+ Convert a representation node to a Python object.
417
+ """
418
+ return loader.construct_yaml_object(node, cls)
419
+
420
+ @classmethod
421
+ def to_yaml(cls, dumper, data):
422
+ """
423
+ Convert a Python object to a representation node.
424
+ """
425
+ return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
426
+ flow_style=cls.yaml_flow_style)
427
+