outerbounds 0.3.71__py3-none-any.whl → 0.3.75__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 (51) hide show
  1. outerbounds/_vendor/PyYAML.LICENSE +20 -0
  2. outerbounds/_vendor/__init__.py +0 -0
  3. outerbounds/_vendor/_yaml/__init__.py +34 -0
  4. outerbounds/_vendor/click/__init__.py +73 -0
  5. outerbounds/_vendor/click/_compat.py +626 -0
  6. outerbounds/_vendor/click/_termui_impl.py +717 -0
  7. outerbounds/_vendor/click/_textwrap.py +49 -0
  8. outerbounds/_vendor/click/_winconsole.py +279 -0
  9. outerbounds/_vendor/click/core.py +2998 -0
  10. outerbounds/_vendor/click/decorators.py +497 -0
  11. outerbounds/_vendor/click/exceptions.py +287 -0
  12. outerbounds/_vendor/click/formatting.py +301 -0
  13. outerbounds/_vendor/click/globals.py +68 -0
  14. outerbounds/_vendor/click/parser.py +529 -0
  15. outerbounds/_vendor/click/py.typed +0 -0
  16. outerbounds/_vendor/click/shell_completion.py +580 -0
  17. outerbounds/_vendor/click/termui.py +787 -0
  18. outerbounds/_vendor/click/testing.py +479 -0
  19. outerbounds/_vendor/click/types.py +1073 -0
  20. outerbounds/_vendor/click/utils.py +580 -0
  21. outerbounds/_vendor/click.LICENSE +28 -0
  22. outerbounds/_vendor/vendor_any.txt +2 -0
  23. outerbounds/_vendor/yaml/__init__.py +471 -0
  24. outerbounds/_vendor/yaml/composer.py +146 -0
  25. outerbounds/_vendor/yaml/constructor.py +862 -0
  26. outerbounds/_vendor/yaml/cyaml.py +177 -0
  27. outerbounds/_vendor/yaml/dumper.py +138 -0
  28. outerbounds/_vendor/yaml/emitter.py +1239 -0
  29. outerbounds/_vendor/yaml/error.py +94 -0
  30. outerbounds/_vendor/yaml/events.py +104 -0
  31. outerbounds/_vendor/yaml/loader.py +62 -0
  32. outerbounds/_vendor/yaml/nodes.py +51 -0
  33. outerbounds/_vendor/yaml/parser.py +629 -0
  34. outerbounds/_vendor/yaml/reader.py +208 -0
  35. outerbounds/_vendor/yaml/representer.py +378 -0
  36. outerbounds/_vendor/yaml/resolver.py +245 -0
  37. outerbounds/_vendor/yaml/scanner.py +1555 -0
  38. outerbounds/_vendor/yaml/serializer.py +127 -0
  39. outerbounds/_vendor/yaml/tokens.py +129 -0
  40. outerbounds/command_groups/cli.py +1 -1
  41. outerbounds/command_groups/local_setup_cli.py +1 -2
  42. outerbounds/command_groups/perimeters_cli.py +1 -2
  43. outerbounds/command_groups/workstations_cli.py +2 -2
  44. outerbounds/utils/kubeconfig.py +2 -2
  45. outerbounds/utils/metaflowconfig.py +1 -1
  46. outerbounds/vendor.py +159 -0
  47. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/METADATA +1 -3
  48. outerbounds-0.3.75.dist-info/RECORD +55 -0
  49. outerbounds-0.3.71.dist-info/RECORD +0 -15
  50. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/WHEEL +0 -0
  51. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1239 @@
1
+ # Emitter expects events obeying the following grammar:
2
+ # stream ::= STREAM-START document* STREAM-END
3
+ # document ::= DOCUMENT-START node DOCUMENT-END
4
+ # node ::= SCALAR | sequence | mapping
5
+ # sequence ::= SEQUENCE-START node* SEQUENCE-END
6
+ # mapping ::= MAPPING-START (node node)* MAPPING-END
7
+
8
+ __all__ = ["Emitter", "EmitterError"]
9
+
10
+ from .error import YAMLError
11
+ from .events import *
12
+
13
+
14
+ class EmitterError(YAMLError):
15
+ pass
16
+
17
+
18
+ class ScalarAnalysis:
19
+ def __init__(
20
+ self,
21
+ scalar,
22
+ empty,
23
+ multiline,
24
+ allow_flow_plain,
25
+ allow_block_plain,
26
+ allow_single_quoted,
27
+ allow_double_quoted,
28
+ allow_block,
29
+ ):
30
+ self.scalar = scalar
31
+ self.empty = empty
32
+ self.multiline = multiline
33
+ self.allow_flow_plain = allow_flow_plain
34
+ self.allow_block_plain = allow_block_plain
35
+ self.allow_single_quoted = allow_single_quoted
36
+ self.allow_double_quoted = allow_double_quoted
37
+ self.allow_block = allow_block
38
+
39
+
40
+ class Emitter:
41
+
42
+ DEFAULT_TAG_PREFIXES = {
43
+ "!": "!",
44
+ "tag:yaml.org,2002:": "!!",
45
+ }
46
+
47
+ def __init__(
48
+ self,
49
+ stream,
50
+ canonical=None,
51
+ indent=None,
52
+ width=None,
53
+ allow_unicode=None,
54
+ line_break=None,
55
+ ):
56
+
57
+ # The stream should have the methods `write` and possibly `flush`.
58
+ self.stream = stream
59
+
60
+ # Encoding can be overridden by STREAM-START.
61
+ self.encoding = None
62
+
63
+ # Emitter is a state machine with a stack of states to handle nested
64
+ # structures.
65
+ self.states = []
66
+ self.state = self.expect_stream_start
67
+
68
+ # Current event and the event queue.
69
+ self.events = []
70
+ self.event = None
71
+
72
+ # The current indentation level and the stack of previous indents.
73
+ self.indents = []
74
+ self.indent = None
75
+
76
+ # Flow level.
77
+ self.flow_level = 0
78
+
79
+ # Contexts.
80
+ self.root_context = False
81
+ self.sequence_context = False
82
+ self.mapping_context = False
83
+ self.simple_key_context = False
84
+
85
+ # Characteristics of the last emitted character:
86
+ # - current position.
87
+ # - is it a whitespace?
88
+ # - is it an indention character
89
+ # (indentation space, '-', '?', or ':')?
90
+ self.line = 0
91
+ self.column = 0
92
+ self.whitespace = True
93
+ self.indention = True
94
+
95
+ # Whether the document requires an explicit document indicator
96
+ self.open_ended = False
97
+
98
+ # Formatting details.
99
+ self.canonical = canonical
100
+ self.allow_unicode = allow_unicode
101
+ self.best_indent = 2
102
+ if indent and 1 < indent < 10:
103
+ self.best_indent = indent
104
+ self.best_width = 80
105
+ if width and width > self.best_indent * 2:
106
+ self.best_width = width
107
+ self.best_line_break = "\n"
108
+ if line_break in ["\r", "\n", "\r\n"]:
109
+ self.best_line_break = line_break
110
+
111
+ # Tag prefixes.
112
+ self.tag_prefixes = None
113
+
114
+ # Prepared anchor and tag.
115
+ self.prepared_anchor = None
116
+ self.prepared_tag = None
117
+
118
+ # Scalar analysis and style.
119
+ self.analysis = None
120
+ self.style = None
121
+
122
+ def dispose(self):
123
+ # Reset the state attributes (to clear self-references)
124
+ self.states = []
125
+ self.state = None
126
+
127
+ def emit(self, event):
128
+ self.events.append(event)
129
+ while not self.need_more_events():
130
+ self.event = self.events.pop(0)
131
+ self.state()
132
+ self.event = None
133
+
134
+ # In some cases, we wait for a few next events before emitting.
135
+
136
+ def need_more_events(self):
137
+ if not self.events:
138
+ return True
139
+ event = self.events[0]
140
+ if isinstance(event, DocumentStartEvent):
141
+ return self.need_events(1)
142
+ elif isinstance(event, SequenceStartEvent):
143
+ return self.need_events(2)
144
+ elif isinstance(event, MappingStartEvent):
145
+ return self.need_events(3)
146
+ else:
147
+ return False
148
+
149
+ def need_events(self, count):
150
+ level = 0
151
+ for event in self.events[1:]:
152
+ if isinstance(event, (DocumentStartEvent, CollectionStartEvent)):
153
+ level += 1
154
+ elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)):
155
+ level -= 1
156
+ elif isinstance(event, StreamEndEvent):
157
+ level = -1
158
+ if level < 0:
159
+ return False
160
+ return len(self.events) < count + 1
161
+
162
+ def increase_indent(self, flow=False, indentless=False):
163
+ self.indents.append(self.indent)
164
+ if self.indent is None:
165
+ if flow:
166
+ self.indent = self.best_indent
167
+ else:
168
+ self.indent = 0
169
+ elif not indentless:
170
+ self.indent += self.best_indent
171
+
172
+ # States.
173
+
174
+ # Stream handlers.
175
+
176
+ def expect_stream_start(self):
177
+ if isinstance(self.event, StreamStartEvent):
178
+ if self.event.encoding and not hasattr(self.stream, "encoding"):
179
+ self.encoding = self.event.encoding
180
+ self.write_stream_start()
181
+ self.state = self.expect_first_document_start
182
+ else:
183
+ raise EmitterError("expected StreamStartEvent, but got %s" % self.event)
184
+
185
+ def expect_nothing(self):
186
+ raise EmitterError("expected nothing, but got %s" % self.event)
187
+
188
+ # Document handlers.
189
+
190
+ def expect_first_document_start(self):
191
+ return self.expect_document_start(first=True)
192
+
193
+ def expect_document_start(self, first=False):
194
+ if isinstance(self.event, DocumentStartEvent):
195
+ if (self.event.version or self.event.tags) and self.open_ended:
196
+ self.write_indicator("...", True)
197
+ self.write_indent()
198
+ if self.event.version:
199
+ version_text = self.prepare_version(self.event.version)
200
+ self.write_version_directive(version_text)
201
+ self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy()
202
+ if self.event.tags:
203
+ handles = sorted(self.event.tags.keys())
204
+ for handle in handles:
205
+ prefix = self.event.tags[handle]
206
+ self.tag_prefixes[prefix] = handle
207
+ handle_text = self.prepare_tag_handle(handle)
208
+ prefix_text = self.prepare_tag_prefix(prefix)
209
+ self.write_tag_directive(handle_text, prefix_text)
210
+ implicit = (
211
+ first
212
+ and not self.event.explicit
213
+ and not self.canonical
214
+ and not self.event.version
215
+ and not self.event.tags
216
+ and not self.check_empty_document()
217
+ )
218
+ if not implicit:
219
+ self.write_indent()
220
+ self.write_indicator("---", True)
221
+ if self.canonical:
222
+ self.write_indent()
223
+ self.state = self.expect_document_root
224
+ elif isinstance(self.event, StreamEndEvent):
225
+ if self.open_ended:
226
+ self.write_indicator("...", True)
227
+ self.write_indent()
228
+ self.write_stream_end()
229
+ self.state = self.expect_nothing
230
+ else:
231
+ raise EmitterError("expected DocumentStartEvent, but got %s" % self.event)
232
+
233
+ def expect_document_end(self):
234
+ if isinstance(self.event, DocumentEndEvent):
235
+ self.write_indent()
236
+ if self.event.explicit:
237
+ self.write_indicator("...", True)
238
+ self.write_indent()
239
+ self.flush_stream()
240
+ self.state = self.expect_document_start
241
+ else:
242
+ raise EmitterError("expected DocumentEndEvent, but got %s" % self.event)
243
+
244
+ def expect_document_root(self):
245
+ self.states.append(self.expect_document_end)
246
+ self.expect_node(root=True)
247
+
248
+ # Node handlers.
249
+
250
+ def expect_node(self, root=False, sequence=False, mapping=False, simple_key=False):
251
+ self.root_context = root
252
+ self.sequence_context = sequence
253
+ self.mapping_context = mapping
254
+ self.simple_key_context = simple_key
255
+ if isinstance(self.event, AliasEvent):
256
+ self.expect_alias()
257
+ elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)):
258
+ self.process_anchor("&")
259
+ self.process_tag()
260
+ if isinstance(self.event, ScalarEvent):
261
+ self.expect_scalar()
262
+ elif isinstance(self.event, SequenceStartEvent):
263
+ if (
264
+ self.flow_level
265
+ or self.canonical
266
+ or self.event.flow_style
267
+ or self.check_empty_sequence()
268
+ ):
269
+ self.expect_flow_sequence()
270
+ else:
271
+ self.expect_block_sequence()
272
+ elif isinstance(self.event, MappingStartEvent):
273
+ if (
274
+ self.flow_level
275
+ or self.canonical
276
+ or self.event.flow_style
277
+ or self.check_empty_mapping()
278
+ ):
279
+ self.expect_flow_mapping()
280
+ else:
281
+ self.expect_block_mapping()
282
+ else:
283
+ raise EmitterError("expected NodeEvent, but got %s" % self.event)
284
+
285
+ def expect_alias(self):
286
+ if self.event.anchor is None:
287
+ raise EmitterError("anchor is not specified for alias")
288
+ self.process_anchor("*")
289
+ self.state = self.states.pop()
290
+
291
+ def expect_scalar(self):
292
+ self.increase_indent(flow=True)
293
+ self.process_scalar()
294
+ self.indent = self.indents.pop()
295
+ self.state = self.states.pop()
296
+
297
+ # Flow sequence handlers.
298
+
299
+ def expect_flow_sequence(self):
300
+ self.write_indicator("[", True, whitespace=True)
301
+ self.flow_level += 1
302
+ self.increase_indent(flow=True)
303
+ self.state = self.expect_first_flow_sequence_item
304
+
305
+ def expect_first_flow_sequence_item(self):
306
+ if isinstance(self.event, SequenceEndEvent):
307
+ self.indent = self.indents.pop()
308
+ self.flow_level -= 1
309
+ self.write_indicator("]", False)
310
+ self.state = self.states.pop()
311
+ else:
312
+ if self.canonical or self.column > self.best_width:
313
+ self.write_indent()
314
+ self.states.append(self.expect_flow_sequence_item)
315
+ self.expect_node(sequence=True)
316
+
317
+ def expect_flow_sequence_item(self):
318
+ if isinstance(self.event, SequenceEndEvent):
319
+ self.indent = self.indents.pop()
320
+ self.flow_level -= 1
321
+ if self.canonical:
322
+ self.write_indicator(",", False)
323
+ self.write_indent()
324
+ self.write_indicator("]", False)
325
+ self.state = self.states.pop()
326
+ else:
327
+ self.write_indicator(",", False)
328
+ if self.canonical or self.column > self.best_width:
329
+ self.write_indent()
330
+ self.states.append(self.expect_flow_sequence_item)
331
+ self.expect_node(sequence=True)
332
+
333
+ # Flow mapping handlers.
334
+
335
+ def expect_flow_mapping(self):
336
+ self.write_indicator("{", True, whitespace=True)
337
+ self.flow_level += 1
338
+ self.increase_indent(flow=True)
339
+ self.state = self.expect_first_flow_mapping_key
340
+
341
+ def expect_first_flow_mapping_key(self):
342
+ if isinstance(self.event, MappingEndEvent):
343
+ self.indent = self.indents.pop()
344
+ self.flow_level -= 1
345
+ self.write_indicator("}", False)
346
+ self.state = self.states.pop()
347
+ else:
348
+ if self.canonical or self.column > self.best_width:
349
+ self.write_indent()
350
+ if not self.canonical and self.check_simple_key():
351
+ self.states.append(self.expect_flow_mapping_simple_value)
352
+ self.expect_node(mapping=True, simple_key=True)
353
+ else:
354
+ self.write_indicator("?", True)
355
+ self.states.append(self.expect_flow_mapping_value)
356
+ self.expect_node(mapping=True)
357
+
358
+ def expect_flow_mapping_key(self):
359
+ if isinstance(self.event, MappingEndEvent):
360
+ self.indent = self.indents.pop()
361
+ self.flow_level -= 1
362
+ if self.canonical:
363
+ self.write_indicator(",", False)
364
+ self.write_indent()
365
+ self.write_indicator("}", False)
366
+ self.state = self.states.pop()
367
+ else:
368
+ self.write_indicator(",", False)
369
+ if self.canonical or self.column > self.best_width:
370
+ self.write_indent()
371
+ if not self.canonical and self.check_simple_key():
372
+ self.states.append(self.expect_flow_mapping_simple_value)
373
+ self.expect_node(mapping=True, simple_key=True)
374
+ else:
375
+ self.write_indicator("?", True)
376
+ self.states.append(self.expect_flow_mapping_value)
377
+ self.expect_node(mapping=True)
378
+
379
+ def expect_flow_mapping_simple_value(self):
380
+ self.write_indicator(":", False)
381
+ self.states.append(self.expect_flow_mapping_key)
382
+ self.expect_node(mapping=True)
383
+
384
+ def expect_flow_mapping_value(self):
385
+ if self.canonical or self.column > self.best_width:
386
+ self.write_indent()
387
+ self.write_indicator(":", True)
388
+ self.states.append(self.expect_flow_mapping_key)
389
+ self.expect_node(mapping=True)
390
+
391
+ # Block sequence handlers.
392
+
393
+ def expect_block_sequence(self):
394
+ indentless = self.mapping_context and not self.indention
395
+ self.increase_indent(flow=False, indentless=indentless)
396
+ self.state = self.expect_first_block_sequence_item
397
+
398
+ def expect_first_block_sequence_item(self):
399
+ return self.expect_block_sequence_item(first=True)
400
+
401
+ def expect_block_sequence_item(self, first=False):
402
+ if not first and isinstance(self.event, SequenceEndEvent):
403
+ self.indent = self.indents.pop()
404
+ self.state = self.states.pop()
405
+ else:
406
+ self.write_indent()
407
+ self.write_indicator("-", True, indention=True)
408
+ self.states.append(self.expect_block_sequence_item)
409
+ self.expect_node(sequence=True)
410
+
411
+ # Block mapping handlers.
412
+
413
+ def expect_block_mapping(self):
414
+ self.increase_indent(flow=False)
415
+ self.state = self.expect_first_block_mapping_key
416
+
417
+ def expect_first_block_mapping_key(self):
418
+ return self.expect_block_mapping_key(first=True)
419
+
420
+ def expect_block_mapping_key(self, first=False):
421
+ if not first and isinstance(self.event, MappingEndEvent):
422
+ self.indent = self.indents.pop()
423
+ self.state = self.states.pop()
424
+ else:
425
+ self.write_indent()
426
+ if self.check_simple_key():
427
+ self.states.append(self.expect_block_mapping_simple_value)
428
+ self.expect_node(mapping=True, simple_key=True)
429
+ else:
430
+ self.write_indicator("?", True, indention=True)
431
+ self.states.append(self.expect_block_mapping_value)
432
+ self.expect_node(mapping=True)
433
+
434
+ def expect_block_mapping_simple_value(self):
435
+ self.write_indicator(":", False)
436
+ self.states.append(self.expect_block_mapping_key)
437
+ self.expect_node(mapping=True)
438
+
439
+ def expect_block_mapping_value(self):
440
+ self.write_indent()
441
+ self.write_indicator(":", True, indention=True)
442
+ self.states.append(self.expect_block_mapping_key)
443
+ self.expect_node(mapping=True)
444
+
445
+ # Checkers.
446
+
447
+ def check_empty_sequence(self):
448
+ return (
449
+ isinstance(self.event, SequenceStartEvent)
450
+ and self.events
451
+ and isinstance(self.events[0], SequenceEndEvent)
452
+ )
453
+
454
+ def check_empty_mapping(self):
455
+ return (
456
+ isinstance(self.event, MappingStartEvent)
457
+ and self.events
458
+ and isinstance(self.events[0], MappingEndEvent)
459
+ )
460
+
461
+ def check_empty_document(self):
462
+ if not isinstance(self.event, DocumentStartEvent) or not self.events:
463
+ return False
464
+ event = self.events[0]
465
+ return (
466
+ isinstance(event, ScalarEvent)
467
+ and event.anchor is None
468
+ and event.tag is None
469
+ and event.implicit
470
+ and event.value == ""
471
+ )
472
+
473
+ def check_simple_key(self):
474
+ length = 0
475
+ if isinstance(self.event, NodeEvent) and self.event.anchor is not None:
476
+ if self.prepared_anchor is None:
477
+ self.prepared_anchor = self.prepare_anchor(self.event.anchor)
478
+ length += len(self.prepared_anchor)
479
+ if (
480
+ isinstance(self.event, (ScalarEvent, CollectionStartEvent))
481
+ and self.event.tag is not None
482
+ ):
483
+ if self.prepared_tag is None:
484
+ self.prepared_tag = self.prepare_tag(self.event.tag)
485
+ length += len(self.prepared_tag)
486
+ if isinstance(self.event, ScalarEvent):
487
+ if self.analysis is None:
488
+ self.analysis = self.analyze_scalar(self.event.value)
489
+ length += len(self.analysis.scalar)
490
+ return length < 128 and (
491
+ isinstance(self.event, AliasEvent)
492
+ or (
493
+ isinstance(self.event, ScalarEvent)
494
+ and not self.analysis.empty
495
+ and not self.analysis.multiline
496
+ )
497
+ or self.check_empty_sequence()
498
+ or self.check_empty_mapping()
499
+ )
500
+
501
+ # Anchor, Tag, and Scalar processors.
502
+
503
+ def process_anchor(self, indicator):
504
+ if self.event.anchor is None:
505
+ self.prepared_anchor = None
506
+ return
507
+ if self.prepared_anchor is None:
508
+ self.prepared_anchor = self.prepare_anchor(self.event.anchor)
509
+ if self.prepared_anchor:
510
+ self.write_indicator(indicator + self.prepared_anchor, True)
511
+ self.prepared_anchor = None
512
+
513
+ def process_tag(self):
514
+ tag = self.event.tag
515
+ if isinstance(self.event, ScalarEvent):
516
+ if self.style is None:
517
+ self.style = self.choose_scalar_style()
518
+ if (not self.canonical or tag is None) and (
519
+ (self.style == "" and self.event.implicit[0])
520
+ or (self.style != "" and self.event.implicit[1])
521
+ ):
522
+ self.prepared_tag = None
523
+ return
524
+ if self.event.implicit[0] and tag is None:
525
+ tag = "!"
526
+ self.prepared_tag = None
527
+ else:
528
+ if (not self.canonical or tag is None) and self.event.implicit:
529
+ self.prepared_tag = None
530
+ return
531
+ if tag is None:
532
+ raise EmitterError("tag is not specified")
533
+ if self.prepared_tag is None:
534
+ self.prepared_tag = self.prepare_tag(tag)
535
+ if self.prepared_tag:
536
+ self.write_indicator(self.prepared_tag, True)
537
+ self.prepared_tag = None
538
+
539
+ def choose_scalar_style(self):
540
+ if self.analysis is None:
541
+ self.analysis = self.analyze_scalar(self.event.value)
542
+ if self.event.style == '"' or self.canonical:
543
+ return '"'
544
+ if not self.event.style and self.event.implicit[0]:
545
+ if not (
546
+ self.simple_key_context
547
+ and (self.analysis.empty or self.analysis.multiline)
548
+ ) and (
549
+ self.flow_level
550
+ and self.analysis.allow_flow_plain
551
+ or (not self.flow_level and self.analysis.allow_block_plain)
552
+ ):
553
+ return ""
554
+ if self.event.style and self.event.style in "|>":
555
+ if (
556
+ not self.flow_level
557
+ and not self.simple_key_context
558
+ and self.analysis.allow_block
559
+ ):
560
+ return self.event.style
561
+ if not self.event.style or self.event.style == "'":
562
+ if self.analysis.allow_single_quoted and not (
563
+ self.simple_key_context and self.analysis.multiline
564
+ ):
565
+ return "'"
566
+ return '"'
567
+
568
+ def process_scalar(self):
569
+ if self.analysis is None:
570
+ self.analysis = self.analyze_scalar(self.event.value)
571
+ if self.style is None:
572
+ self.style = self.choose_scalar_style()
573
+ split = not self.simple_key_context
574
+ # if self.analysis.multiline and split \
575
+ # and (not self.style or self.style in '\'\"'):
576
+ # self.write_indent()
577
+ if self.style == '"':
578
+ self.write_double_quoted(self.analysis.scalar, split)
579
+ elif self.style == "'":
580
+ self.write_single_quoted(self.analysis.scalar, split)
581
+ elif self.style == ">":
582
+ self.write_folded(self.analysis.scalar)
583
+ elif self.style == "|":
584
+ self.write_literal(self.analysis.scalar)
585
+ else:
586
+ self.write_plain(self.analysis.scalar, split)
587
+ self.analysis = None
588
+ self.style = None
589
+
590
+ # Analyzers.
591
+
592
+ def prepare_version(self, version):
593
+ major, minor = version
594
+ if major != 1:
595
+ raise EmitterError("unsupported YAML version: %d.%d" % (major, minor))
596
+ return "%d.%d" % (major, minor)
597
+
598
+ def prepare_tag_handle(self, handle):
599
+ if not handle:
600
+ raise EmitterError("tag handle must not be empty")
601
+ if handle[0] != "!" or handle[-1] != "!":
602
+ raise EmitterError("tag handle must start and end with '!': %r" % handle)
603
+ for ch in handle[1:-1]:
604
+ if not (
605
+ "0" <= ch <= "9" or "A" <= ch <= "Z" or "a" <= ch <= "z" or ch in "-_"
606
+ ):
607
+ raise EmitterError(
608
+ "invalid character %r in the tag handle: %r" % (ch, handle)
609
+ )
610
+ return handle
611
+
612
+ def prepare_tag_prefix(self, prefix):
613
+ if not prefix:
614
+ raise EmitterError("tag prefix must not be empty")
615
+ chunks = []
616
+ start = end = 0
617
+ if prefix[0] == "!":
618
+ end = 1
619
+ while end < len(prefix):
620
+ ch = prefix[end]
621
+ if (
622
+ "0" <= ch <= "9"
623
+ or "A" <= ch <= "Z"
624
+ or "a" <= ch <= "z"
625
+ or ch in "-;/?!:@&=+$,_.~*'()[]"
626
+ ):
627
+ end += 1
628
+ else:
629
+ if start < end:
630
+ chunks.append(prefix[start:end])
631
+ start = end = end + 1
632
+ data = ch.encode("utf-8")
633
+ for ch in data:
634
+ chunks.append("%%%02X" % ord(ch))
635
+ if start < end:
636
+ chunks.append(prefix[start:end])
637
+ return "".join(chunks)
638
+
639
+ def prepare_tag(self, tag):
640
+ if not tag:
641
+ raise EmitterError("tag must not be empty")
642
+ if tag == "!":
643
+ return tag
644
+ handle = None
645
+ suffix = tag
646
+ prefixes = sorted(self.tag_prefixes.keys())
647
+ for prefix in prefixes:
648
+ if tag.startswith(prefix) and (prefix == "!" or len(prefix) < len(tag)):
649
+ handle = self.tag_prefixes[prefix]
650
+ suffix = tag[len(prefix) :]
651
+ chunks = []
652
+ start = end = 0
653
+ while end < len(suffix):
654
+ ch = suffix[end]
655
+ if (
656
+ "0" <= ch <= "9"
657
+ or "A" <= ch <= "Z"
658
+ or "a" <= ch <= "z"
659
+ or ch in "-;/?:@&=+$,_.~*'()[]"
660
+ or (ch == "!" and handle != "!")
661
+ ):
662
+ end += 1
663
+ else:
664
+ if start < end:
665
+ chunks.append(suffix[start:end])
666
+ start = end = end + 1
667
+ data = ch.encode("utf-8")
668
+ for ch in data:
669
+ chunks.append("%%%02X" % ch)
670
+ if start < end:
671
+ chunks.append(suffix[start:end])
672
+ suffix_text = "".join(chunks)
673
+ if handle:
674
+ return "%s%s" % (handle, suffix_text)
675
+ else:
676
+ return "!<%s>" % suffix_text
677
+
678
+ def prepare_anchor(self, anchor):
679
+ if not anchor:
680
+ raise EmitterError("anchor must not be empty")
681
+ for ch in anchor:
682
+ if not (
683
+ "0" <= ch <= "9" or "A" <= ch <= "Z" or "a" <= ch <= "z" or ch in "-_"
684
+ ):
685
+ raise EmitterError(
686
+ "invalid character %r in the anchor: %r" % (ch, anchor)
687
+ )
688
+ return anchor
689
+
690
+ def analyze_scalar(self, scalar):
691
+
692
+ # Empty scalar is a special case.
693
+ if not scalar:
694
+ return ScalarAnalysis(
695
+ scalar=scalar,
696
+ empty=True,
697
+ multiline=False,
698
+ allow_flow_plain=False,
699
+ allow_block_plain=True,
700
+ allow_single_quoted=True,
701
+ allow_double_quoted=True,
702
+ allow_block=False,
703
+ )
704
+
705
+ # Indicators and special characters.
706
+ block_indicators = False
707
+ flow_indicators = False
708
+ line_breaks = False
709
+ special_characters = False
710
+
711
+ # Important whitespace combinations.
712
+ leading_space = False
713
+ leading_break = False
714
+ trailing_space = False
715
+ trailing_break = False
716
+ break_space = False
717
+ space_break = False
718
+
719
+ # Check document indicators.
720
+ if scalar.startswith("---") or scalar.startswith("..."):
721
+ block_indicators = True
722
+ flow_indicators = True
723
+
724
+ # First character or preceded by a whitespace.
725
+ preceded_by_whitespace = True
726
+
727
+ # Last character or followed by a whitespace.
728
+ followed_by_whitespace = (
729
+ len(scalar) == 1 or scalar[1] in "\0 \t\r\n\x85\u2028\u2029"
730
+ )
731
+
732
+ # The previous character is a space.
733
+ previous_space = False
734
+
735
+ # The previous character is a break.
736
+ previous_break = False
737
+
738
+ index = 0
739
+ while index < len(scalar):
740
+ ch = scalar[index]
741
+
742
+ # Check for indicators.
743
+ if index == 0:
744
+ # Leading indicators are special characters.
745
+ if ch in "#,[]{}&*!|>'\"%@`":
746
+ flow_indicators = True
747
+ block_indicators = True
748
+ if ch in "?:":
749
+ flow_indicators = True
750
+ if followed_by_whitespace:
751
+ block_indicators = True
752
+ if ch == "-" and followed_by_whitespace:
753
+ flow_indicators = True
754
+ block_indicators = True
755
+ else:
756
+ # Some indicators cannot appear within a scalar as well.
757
+ if ch in ",?[]{}":
758
+ flow_indicators = True
759
+ if ch == ":":
760
+ flow_indicators = True
761
+ if followed_by_whitespace:
762
+ block_indicators = True
763
+ if ch == "#" and preceded_by_whitespace:
764
+ flow_indicators = True
765
+ block_indicators = True
766
+
767
+ # Check for line breaks, special, and unicode characters.
768
+ if ch in "\n\x85\u2028\u2029":
769
+ line_breaks = True
770
+ if not (ch == "\n" or "\x20" <= ch <= "\x7E"):
771
+ if (
772
+ ch == "\x85"
773
+ or "\xA0" <= ch <= "\uD7FF"
774
+ or "\uE000" <= ch <= "\uFFFD"
775
+ or "\U00010000" <= ch < "\U0010ffff"
776
+ ) and ch != "\uFEFF":
777
+ unicode_characters = True
778
+ if not self.allow_unicode:
779
+ special_characters = True
780
+ else:
781
+ special_characters = True
782
+
783
+ # Detect important whitespace combinations.
784
+ if ch == " ":
785
+ if index == 0:
786
+ leading_space = True
787
+ if index == len(scalar) - 1:
788
+ trailing_space = True
789
+ if previous_break:
790
+ break_space = True
791
+ previous_space = True
792
+ previous_break = False
793
+ elif ch in "\n\x85\u2028\u2029":
794
+ if index == 0:
795
+ leading_break = True
796
+ if index == len(scalar) - 1:
797
+ trailing_break = True
798
+ if previous_space:
799
+ space_break = True
800
+ previous_space = False
801
+ previous_break = True
802
+ else:
803
+ previous_space = False
804
+ previous_break = False
805
+
806
+ # Prepare for the next character.
807
+ index += 1
808
+ preceded_by_whitespace = ch in "\0 \t\r\n\x85\u2028\u2029"
809
+ followed_by_whitespace = (
810
+ index + 1 >= len(scalar)
811
+ or scalar[index + 1] in "\0 \t\r\n\x85\u2028\u2029"
812
+ )
813
+
814
+ # Let's decide what styles are allowed.
815
+ allow_flow_plain = True
816
+ allow_block_plain = True
817
+ allow_single_quoted = True
818
+ allow_double_quoted = True
819
+ allow_block = True
820
+
821
+ # Leading and trailing whitespaces are bad for plain scalars.
822
+ if leading_space or leading_break or trailing_space or trailing_break:
823
+ allow_flow_plain = allow_block_plain = False
824
+
825
+ # We do not permit trailing spaces for block scalars.
826
+ if trailing_space:
827
+ allow_block = False
828
+
829
+ # Spaces at the beginning of a new line are only acceptable for block
830
+ # scalars.
831
+ if break_space:
832
+ allow_flow_plain = allow_block_plain = allow_single_quoted = False
833
+
834
+ # Spaces followed by breaks, as well as special character are only
835
+ # allowed for double quoted scalars.
836
+ if space_break or special_characters:
837
+ allow_flow_plain = (
838
+ allow_block_plain
839
+ ) = allow_single_quoted = allow_block = False
840
+
841
+ # Although the plain scalar writer supports breaks, we never emit
842
+ # multiline plain scalars.
843
+ if line_breaks:
844
+ allow_flow_plain = allow_block_plain = False
845
+
846
+ # Flow indicators are forbidden for flow plain scalars.
847
+ if flow_indicators:
848
+ allow_flow_plain = False
849
+
850
+ # Block indicators are forbidden for block plain scalars.
851
+ if block_indicators:
852
+ allow_block_plain = False
853
+
854
+ return ScalarAnalysis(
855
+ scalar=scalar,
856
+ empty=False,
857
+ multiline=line_breaks,
858
+ allow_flow_plain=allow_flow_plain,
859
+ allow_block_plain=allow_block_plain,
860
+ allow_single_quoted=allow_single_quoted,
861
+ allow_double_quoted=allow_double_quoted,
862
+ allow_block=allow_block,
863
+ )
864
+
865
+ # Writers.
866
+
867
+ def flush_stream(self):
868
+ if hasattr(self.stream, "flush"):
869
+ self.stream.flush()
870
+
871
+ def write_stream_start(self):
872
+ # Write BOM if needed.
873
+ if self.encoding and self.encoding.startswith("utf-16"):
874
+ self.stream.write("\uFEFF".encode(self.encoding))
875
+
876
+ def write_stream_end(self):
877
+ self.flush_stream()
878
+
879
+ def write_indicator(
880
+ self, indicator, need_whitespace, whitespace=False, indention=False
881
+ ):
882
+ if self.whitespace or not need_whitespace:
883
+ data = indicator
884
+ else:
885
+ data = " " + indicator
886
+ self.whitespace = whitespace
887
+ self.indention = self.indention and indention
888
+ self.column += len(data)
889
+ self.open_ended = False
890
+ if self.encoding:
891
+ data = data.encode(self.encoding)
892
+ self.stream.write(data)
893
+
894
+ def write_indent(self):
895
+ indent = self.indent or 0
896
+ if (
897
+ not self.indention
898
+ or self.column > indent
899
+ or (self.column == indent and not self.whitespace)
900
+ ):
901
+ self.write_line_break()
902
+ if self.column < indent:
903
+ self.whitespace = True
904
+ data = " " * (indent - self.column)
905
+ self.column = indent
906
+ if self.encoding:
907
+ data = data.encode(self.encoding)
908
+ self.stream.write(data)
909
+
910
+ def write_line_break(self, data=None):
911
+ if data is None:
912
+ data = self.best_line_break
913
+ self.whitespace = True
914
+ self.indention = True
915
+ self.line += 1
916
+ self.column = 0
917
+ if self.encoding:
918
+ data = data.encode(self.encoding)
919
+ self.stream.write(data)
920
+
921
+ def write_version_directive(self, version_text):
922
+ data = "%%YAML %s" % version_text
923
+ if self.encoding:
924
+ data = data.encode(self.encoding)
925
+ self.stream.write(data)
926
+ self.write_line_break()
927
+
928
+ def write_tag_directive(self, handle_text, prefix_text):
929
+ data = "%%TAG %s %s" % (handle_text, prefix_text)
930
+ if self.encoding:
931
+ data = data.encode(self.encoding)
932
+ self.stream.write(data)
933
+ self.write_line_break()
934
+
935
+ # Scalar streams.
936
+
937
+ def write_single_quoted(self, text, split=True):
938
+ self.write_indicator("'", True)
939
+ spaces = False
940
+ breaks = False
941
+ start = end = 0
942
+ while end <= len(text):
943
+ ch = None
944
+ if end < len(text):
945
+ ch = text[end]
946
+ if spaces:
947
+ if ch is None or ch != " ":
948
+ if (
949
+ start + 1 == end
950
+ and self.column > self.best_width
951
+ and split
952
+ and start != 0
953
+ and end != len(text)
954
+ ):
955
+ self.write_indent()
956
+ else:
957
+ data = text[start:end]
958
+ self.column += len(data)
959
+ if self.encoding:
960
+ data = data.encode(self.encoding)
961
+ self.stream.write(data)
962
+ start = end
963
+ elif breaks:
964
+ if ch is None or ch not in "\n\x85\u2028\u2029":
965
+ if text[start] == "\n":
966
+ self.write_line_break()
967
+ for br in text[start:end]:
968
+ if br == "\n":
969
+ self.write_line_break()
970
+ else:
971
+ self.write_line_break(br)
972
+ self.write_indent()
973
+ start = end
974
+ else:
975
+ if ch is None or ch in " \n\x85\u2028\u2029" or ch == "'":
976
+ if start < end:
977
+ data = text[start:end]
978
+ self.column += len(data)
979
+ if self.encoding:
980
+ data = data.encode(self.encoding)
981
+ self.stream.write(data)
982
+ start = end
983
+ if ch == "'":
984
+ data = "''"
985
+ self.column += 2
986
+ if self.encoding:
987
+ data = data.encode(self.encoding)
988
+ self.stream.write(data)
989
+ start = end + 1
990
+ if ch is not None:
991
+ spaces = ch == " "
992
+ breaks = ch in "\n\x85\u2028\u2029"
993
+ end += 1
994
+ self.write_indicator("'", False)
995
+
996
+ ESCAPE_REPLACEMENTS = {
997
+ "\0": "0",
998
+ "\x07": "a",
999
+ "\x08": "b",
1000
+ "\x09": "t",
1001
+ "\x0A": "n",
1002
+ "\x0B": "v",
1003
+ "\x0C": "f",
1004
+ "\x0D": "r",
1005
+ "\x1B": "e",
1006
+ '"': '"',
1007
+ "\\": "\\",
1008
+ "\x85": "N",
1009
+ "\xA0": "_",
1010
+ "\u2028": "L",
1011
+ "\u2029": "P",
1012
+ }
1013
+
1014
+ def write_double_quoted(self, text, split=True):
1015
+ self.write_indicator('"', True)
1016
+ start = end = 0
1017
+ while end <= len(text):
1018
+ ch = None
1019
+ if end < len(text):
1020
+ ch = text[end]
1021
+ if (
1022
+ ch is None
1023
+ or ch in '"\\\x85\u2028\u2029\uFEFF'
1024
+ or not (
1025
+ "\x20" <= ch <= "\x7E"
1026
+ or (
1027
+ self.allow_unicode
1028
+ and ("\xA0" <= ch <= "\uD7FF" or "\uE000" <= ch <= "\uFFFD")
1029
+ )
1030
+ )
1031
+ ):
1032
+ if start < end:
1033
+ data = text[start:end]
1034
+ self.column += len(data)
1035
+ if self.encoding:
1036
+ data = data.encode(self.encoding)
1037
+ self.stream.write(data)
1038
+ start = end
1039
+ if ch is not None:
1040
+ if ch in self.ESCAPE_REPLACEMENTS:
1041
+ data = "\\" + self.ESCAPE_REPLACEMENTS[ch]
1042
+ elif ch <= "\xFF":
1043
+ data = "\\x%02X" % ord(ch)
1044
+ elif ch <= "\uFFFF":
1045
+ data = "\\u%04X" % ord(ch)
1046
+ else:
1047
+ data = "\\U%08X" % ord(ch)
1048
+ self.column += len(data)
1049
+ if self.encoding:
1050
+ data = data.encode(self.encoding)
1051
+ self.stream.write(data)
1052
+ start = end + 1
1053
+ if (
1054
+ 0 < end < len(text) - 1
1055
+ and (ch == " " or start >= end)
1056
+ and self.column + (end - start) > self.best_width
1057
+ and split
1058
+ ):
1059
+ data = text[start:end] + "\\"
1060
+ if start < end:
1061
+ start = end
1062
+ self.column += len(data)
1063
+ if self.encoding:
1064
+ data = data.encode(self.encoding)
1065
+ self.stream.write(data)
1066
+ self.write_indent()
1067
+ self.whitespace = False
1068
+ self.indention = False
1069
+ if text[start] == " ":
1070
+ data = "\\"
1071
+ self.column += len(data)
1072
+ if self.encoding:
1073
+ data = data.encode(self.encoding)
1074
+ self.stream.write(data)
1075
+ end += 1
1076
+ self.write_indicator('"', False)
1077
+
1078
+ def determine_block_hints(self, text):
1079
+ hints = ""
1080
+ if text:
1081
+ if text[0] in " \n\x85\u2028\u2029":
1082
+ hints += str(self.best_indent)
1083
+ if text[-1] not in "\n\x85\u2028\u2029":
1084
+ hints += "-"
1085
+ elif len(text) == 1 or text[-2] in "\n\x85\u2028\u2029":
1086
+ hints += "+"
1087
+ return hints
1088
+
1089
+ def write_folded(self, text):
1090
+ hints = self.determine_block_hints(text)
1091
+ self.write_indicator(">" + hints, True)
1092
+ if hints[-1:] == "+":
1093
+ self.open_ended = True
1094
+ self.write_line_break()
1095
+ leading_space = True
1096
+ spaces = False
1097
+ breaks = True
1098
+ start = end = 0
1099
+ while end <= len(text):
1100
+ ch = None
1101
+ if end < len(text):
1102
+ ch = text[end]
1103
+ if breaks:
1104
+ if ch is None or ch not in "\n\x85\u2028\u2029":
1105
+ if (
1106
+ not leading_space
1107
+ and ch is not None
1108
+ and ch != " "
1109
+ and text[start] == "\n"
1110
+ ):
1111
+ self.write_line_break()
1112
+ leading_space = ch == " "
1113
+ for br in text[start:end]:
1114
+ if br == "\n":
1115
+ self.write_line_break()
1116
+ else:
1117
+ self.write_line_break(br)
1118
+ if ch is not None:
1119
+ self.write_indent()
1120
+ start = end
1121
+ elif spaces:
1122
+ if ch != " ":
1123
+ if start + 1 == end and self.column > self.best_width:
1124
+ self.write_indent()
1125
+ else:
1126
+ data = text[start:end]
1127
+ self.column += len(data)
1128
+ if self.encoding:
1129
+ data = data.encode(self.encoding)
1130
+ self.stream.write(data)
1131
+ start = end
1132
+ else:
1133
+ if ch is None or ch in " \n\x85\u2028\u2029":
1134
+ data = text[start:end]
1135
+ self.column += len(data)
1136
+ if self.encoding:
1137
+ data = data.encode(self.encoding)
1138
+ self.stream.write(data)
1139
+ if ch is None:
1140
+ self.write_line_break()
1141
+ start = end
1142
+ if ch is not None:
1143
+ breaks = ch in "\n\x85\u2028\u2029"
1144
+ spaces = ch == " "
1145
+ end += 1
1146
+
1147
+ def write_literal(self, text):
1148
+ hints = self.determine_block_hints(text)
1149
+ self.write_indicator("|" + hints, True)
1150
+ if hints[-1:] == "+":
1151
+ self.open_ended = True
1152
+ self.write_line_break()
1153
+ breaks = True
1154
+ start = end = 0
1155
+ while end <= len(text):
1156
+ ch = None
1157
+ if end < len(text):
1158
+ ch = text[end]
1159
+ if breaks:
1160
+ if ch is None or ch not in "\n\x85\u2028\u2029":
1161
+ for br in text[start:end]:
1162
+ if br == "\n":
1163
+ self.write_line_break()
1164
+ else:
1165
+ self.write_line_break(br)
1166
+ if ch is not None:
1167
+ self.write_indent()
1168
+ start = end
1169
+ else:
1170
+ if ch is None or ch in "\n\x85\u2028\u2029":
1171
+ data = text[start:end]
1172
+ if self.encoding:
1173
+ data = data.encode(self.encoding)
1174
+ self.stream.write(data)
1175
+ if ch is None:
1176
+ self.write_line_break()
1177
+ start = end
1178
+ if ch is not None:
1179
+ breaks = ch in "\n\x85\u2028\u2029"
1180
+ end += 1
1181
+
1182
+ def write_plain(self, text, split=True):
1183
+ if self.root_context:
1184
+ self.open_ended = True
1185
+ if not text:
1186
+ return
1187
+ if not self.whitespace:
1188
+ data = " "
1189
+ self.column += len(data)
1190
+ if self.encoding:
1191
+ data = data.encode(self.encoding)
1192
+ self.stream.write(data)
1193
+ self.whitespace = False
1194
+ self.indention = False
1195
+ spaces = False
1196
+ breaks = False
1197
+ start = end = 0
1198
+ while end <= len(text):
1199
+ ch = None
1200
+ if end < len(text):
1201
+ ch = text[end]
1202
+ if spaces:
1203
+ if ch != " ":
1204
+ if start + 1 == end and self.column > self.best_width and split:
1205
+ self.write_indent()
1206
+ self.whitespace = False
1207
+ self.indention = False
1208
+ else:
1209
+ data = text[start:end]
1210
+ self.column += len(data)
1211
+ if self.encoding:
1212
+ data = data.encode(self.encoding)
1213
+ self.stream.write(data)
1214
+ start = end
1215
+ elif breaks:
1216
+ if ch not in "\n\x85\u2028\u2029":
1217
+ if text[start] == "\n":
1218
+ self.write_line_break()
1219
+ for br in text[start:end]:
1220
+ if br == "\n":
1221
+ self.write_line_break()
1222
+ else:
1223
+ self.write_line_break(br)
1224
+ self.write_indent()
1225
+ self.whitespace = False
1226
+ self.indention = False
1227
+ start = end
1228
+ else:
1229
+ if ch is None or ch in " \n\x85\u2028\u2029":
1230
+ data = text[start:end]
1231
+ self.column += len(data)
1232
+ if self.encoding:
1233
+ data = data.encode(self.encoding)
1234
+ self.stream.write(data)
1235
+ start = end
1236
+ if ch is not None:
1237
+ spaces = ch == " "
1238
+ breaks = ch in "\n\x85\u2028\u2029"
1239
+ end += 1