2 # Emitter expects events obeying the following grammar:
3 # stream ::= STREAM-START document* STREAM-END
4 # document ::= DOCUMENT-START node DOCUMENT-END
5 # node ::= SCALAR | sequence | mapping
6 # sequence ::= SEQUENCE-START node* SEQUENCE-END
7 # mapping ::= MAPPING-START (node node)* MAPPING-END
9 __all__ = ['Emitter', 'EmitterError']
11 from error import YAMLError
14 class EmitterError(YAMLError):
17 class ScalarAnalysis(object):
18 def __init__(self, scalar, empty, multiline,
19 allow_flow_plain, allow_block_plain,
20 allow_single_quoted, allow_double_quoted,
24 self.multiline = multiline
25 self.allow_flow_plain = allow_flow_plain
26 self.allow_block_plain = allow_block_plain
27 self.allow_single_quoted = allow_single_quoted
28 self.allow_double_quoted = allow_double_quoted
29 self.allow_block = allow_block
31 class Emitter(object):
33 DEFAULT_TAG_PREFIXES = {
35 u'tag:yaml.org,2002:' : u'!!',
38 def __init__(self, stream, canonical=None, indent=None, width=None,
39 allow_unicode=None, line_break=None):
41 # The stream should have the methods `write` and possibly `flush`.
44 # Encoding can be overriden by STREAM-START.
47 # Emitter is a state machine with a stack of states to handle nested
50 self.state = self.expect_stream_start
52 # Current event and the event queue.
56 # The current indentation level and the stack of previous indents.
64 self.root_context = False
65 self.sequence_context = False
66 self.mapping_context = False
67 self.simple_key_context = False
69 # Characteristics of the last emitted character:
71 # - is it a whitespace?
72 # - is it an indention character
73 # (indentation space, '-', '?', or ':')?
76 self.whitespace = True
79 # Whether the document requires an explicit document indicator
80 self.open_ended = False
83 self.canonical = canonical
84 self.allow_unicode = allow_unicode
86 if indent and 1 < indent < 10:
87 self.best_indent = indent
89 if width and width > self.best_indent*2:
90 self.best_width = width
91 self.best_line_break = u'\n'
92 if line_break in [u'\r', u'\n', u'\r\n']:
93 self.best_line_break = line_break
96 self.tag_prefixes = None
98 # Prepared anchor and tag.
99 self.prepared_anchor = None
100 self.prepared_tag = None
102 # Scalar analysis and style.
107 # Reset the state attributes (to clear self-references)
111 def emit(self, event):
112 self.events.append(event)
113 while not self.need_more_events():
114 self.event = self.events.pop(0)
118 # In some cases, we wait for a few next events before emitting.
120 def need_more_events(self):
123 event = self.events[0]
124 if isinstance(event, DocumentStartEvent):
125 return self.need_events(1)
126 elif isinstance(event, SequenceStartEvent):
127 return self.need_events(2)
128 elif isinstance(event, MappingStartEvent):
129 return self.need_events(3)
133 def need_events(self, count):
135 for event in self.events[1:]:
136 if isinstance(event, (DocumentStartEvent, CollectionStartEvent)):
138 elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)):
140 elif isinstance(event, StreamEndEvent):
144 return (len(self.events) < count+1)
146 def increase_indent(self, flow=False, indentless=False):
147 self.indents.append(self.indent)
148 if self.indent is None:
150 self.indent = self.best_indent
154 self.indent += self.best_indent
160 def expect_stream_start(self):
161 if isinstance(self.event, StreamStartEvent):
162 if self.event.encoding and not getattr(self.stream, 'encoding', None):
163 self.encoding = self.event.encoding
164 self.write_stream_start()
165 self.state = self.expect_first_document_start
167 raise EmitterError("expected StreamStartEvent, but got %s"
170 def expect_nothing(self):
171 raise EmitterError("expected nothing, but got %s" % self.event)
175 def expect_first_document_start(self):
176 return self.expect_document_start(first=True)
178 def expect_document_start(self, first=False):
179 if isinstance(self.event, DocumentStartEvent):
180 if (self.event.version or self.event.tags) and self.open_ended:
181 self.write_indicator(u'...', True)
183 if self.event.version:
184 version_text = self.prepare_version(self.event.version)
185 self.write_version_directive(version_text)
186 self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy()
188 handles = self.event.tags.keys()
190 for handle in handles:
191 prefix = self.event.tags[handle]
192 self.tag_prefixes[prefix] = handle
193 handle_text = self.prepare_tag_handle(handle)
194 prefix_text = self.prepare_tag_prefix(prefix)
195 self.write_tag_directive(handle_text, prefix_text)
196 implicit = (first and not self.event.explicit and not self.canonical
197 and not self.event.version and not self.event.tags
198 and not self.check_empty_document())
201 self.write_indicator(u'---', True)
204 self.state = self.expect_document_root
205 elif isinstance(self.event, StreamEndEvent):
207 self.write_indicator(u'...', True)
209 self.write_stream_end()
210 self.state = self.expect_nothing
212 raise EmitterError("expected DocumentStartEvent, but got %s"
215 def expect_document_end(self):
216 if isinstance(self.event, DocumentEndEvent):
218 if self.event.explicit:
219 self.write_indicator(u'...', True)
222 self.state = self.expect_document_start
224 raise EmitterError("expected DocumentEndEvent, but got %s"
227 def expect_document_root(self):
228 self.states.append(self.expect_document_end)
229 self.expect_node(root=True)
233 def expect_node(self, root=False, sequence=False, mapping=False,
235 self.root_context = root
236 self.sequence_context = sequence
237 self.mapping_context = mapping
238 self.simple_key_context = simple_key
239 if isinstance(self.event, AliasEvent):
241 elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)):
242 self.process_anchor(u'&')
244 if isinstance(self.event, ScalarEvent):
246 elif isinstance(self.event, SequenceStartEvent):
247 if self.flow_level or self.canonical or self.event.flow_style \
248 or self.check_empty_sequence():
249 self.expect_flow_sequence()
251 self.expect_block_sequence()
252 elif isinstance(self.event, MappingStartEvent):
253 if self.flow_level or self.canonical or self.event.flow_style \
254 or self.check_empty_mapping():
255 self.expect_flow_mapping()
257 self.expect_block_mapping()
259 raise EmitterError("expected NodeEvent, but got %s" % self.event)
261 def expect_alias(self):
262 if self.event.anchor is None:
263 raise EmitterError("anchor is not specified for alias")
264 self.process_anchor(u'*')
265 self.state = self.states.pop()
267 def expect_scalar(self):
268 self.increase_indent(flow=True)
269 self.process_scalar()
270 self.indent = self.indents.pop()
271 self.state = self.states.pop()
273 # Flow sequence handlers.
275 def expect_flow_sequence(self):
276 self.write_indicator(u'[', True, whitespace=True)
278 self.increase_indent(flow=True)
279 self.state = self.expect_first_flow_sequence_item
281 def expect_first_flow_sequence_item(self):
282 if isinstance(self.event, SequenceEndEvent):
283 self.indent = self.indents.pop()
285 self.write_indicator(u']', False)
286 self.state = self.states.pop()
288 if self.canonical or self.column > self.best_width:
290 self.states.append(self.expect_flow_sequence_item)
291 self.expect_node(sequence=True)
293 def expect_flow_sequence_item(self):
294 if isinstance(self.event, SequenceEndEvent):
295 self.indent = self.indents.pop()
298 self.write_indicator(u',', False)
300 self.write_indicator(u']', False)
301 self.state = self.states.pop()
303 self.write_indicator(u',', False)
304 if self.canonical or self.column > self.best_width:
306 self.states.append(self.expect_flow_sequence_item)
307 self.expect_node(sequence=True)
309 # Flow mapping handlers.
311 def expect_flow_mapping(self):
312 self.write_indicator(u'{', True, whitespace=True)
314 self.increase_indent(flow=True)
315 self.state = self.expect_first_flow_mapping_key
317 def expect_first_flow_mapping_key(self):
318 if isinstance(self.event, MappingEndEvent):
319 self.indent = self.indents.pop()
321 self.write_indicator(u'}', False)
322 self.state = self.states.pop()
324 if self.canonical or self.column > self.best_width:
326 if not self.canonical and self.check_simple_key():
327 self.states.append(self.expect_flow_mapping_simple_value)
328 self.expect_node(mapping=True, simple_key=True)
330 self.write_indicator(u'?', True)
331 self.states.append(self.expect_flow_mapping_value)
332 self.expect_node(mapping=True)
334 def expect_flow_mapping_key(self):
335 if isinstance(self.event, MappingEndEvent):
336 self.indent = self.indents.pop()
339 self.write_indicator(u',', False)
341 self.write_indicator(u'}', False)
342 self.state = self.states.pop()
344 self.write_indicator(u',', False)
345 if self.canonical or self.column > self.best_width:
347 if not self.canonical and self.check_simple_key():
348 self.states.append(self.expect_flow_mapping_simple_value)
349 self.expect_node(mapping=True, simple_key=True)
351 self.write_indicator(u'?', True)
352 self.states.append(self.expect_flow_mapping_value)
353 self.expect_node(mapping=True)
355 def expect_flow_mapping_simple_value(self):
356 self.write_indicator(u':', False)
357 self.states.append(self.expect_flow_mapping_key)
358 self.expect_node(mapping=True)
360 def expect_flow_mapping_value(self):
361 if self.canonical or self.column > self.best_width:
363 self.write_indicator(u':', True)
364 self.states.append(self.expect_flow_mapping_key)
365 self.expect_node(mapping=True)
367 # Block sequence handlers.
369 def expect_block_sequence(self):
370 indentless = (self.mapping_context and not self.indention)
371 self.increase_indent(flow=False, indentless=indentless)
372 self.state = self.expect_first_block_sequence_item
374 def expect_first_block_sequence_item(self):
375 return self.expect_block_sequence_item(first=True)
377 def expect_block_sequence_item(self, first=False):
378 if not first and isinstance(self.event, SequenceEndEvent):
379 self.indent = self.indents.pop()
380 self.state = self.states.pop()
383 self.write_indicator(u'-', True, indention=True)
384 self.states.append(self.expect_block_sequence_item)
385 self.expect_node(sequence=True)
387 # Block mapping handlers.
389 def expect_block_mapping(self):
390 self.increase_indent(flow=False)
391 self.state = self.expect_first_block_mapping_key
393 def expect_first_block_mapping_key(self):
394 return self.expect_block_mapping_key(first=True)
396 def expect_block_mapping_key(self, first=False):
397 if not first and isinstance(self.event, MappingEndEvent):
398 self.indent = self.indents.pop()
399 self.state = self.states.pop()
402 if self.check_simple_key():
403 self.states.append(self.expect_block_mapping_simple_value)
404 self.expect_node(mapping=True, simple_key=True)
406 self.write_indicator(u'?', True, indention=True)
407 self.states.append(self.expect_block_mapping_value)
408 self.expect_node(mapping=True)
410 def expect_block_mapping_simple_value(self):
411 self.write_indicator(u':', False)
412 self.states.append(self.expect_block_mapping_key)
413 self.expect_node(mapping=True)
415 def expect_block_mapping_value(self):
417 self.write_indicator(u':', True, indention=True)
418 self.states.append(self.expect_block_mapping_key)
419 self.expect_node(mapping=True)
423 def check_empty_sequence(self):
424 return (isinstance(self.event, SequenceStartEvent) and self.events
425 and isinstance(self.events[0], SequenceEndEvent))
427 def check_empty_mapping(self):
428 return (isinstance(self.event, MappingStartEvent) and self.events
429 and isinstance(self.events[0], MappingEndEvent))
431 def check_empty_document(self):
432 if not isinstance(self.event, DocumentStartEvent) or not self.events:
434 event = self.events[0]
435 return (isinstance(event, ScalarEvent) and event.anchor is None
436 and event.tag is None and event.implicit and event.value == u'')
438 def check_simple_key(self):
440 if isinstance(self.event, NodeEvent) and self.event.anchor is not None:
441 if self.prepared_anchor is None:
442 self.prepared_anchor = self.prepare_anchor(self.event.anchor)
443 length += len(self.prepared_anchor)
444 if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \
445 and self.event.tag is not None:
446 if self.prepared_tag is None:
447 self.prepared_tag = self.prepare_tag(self.event.tag)
448 length += len(self.prepared_tag)
449 if isinstance(self.event, ScalarEvent):
450 if self.analysis is None:
451 self.analysis = self.analyze_scalar(self.event.value)
452 length += len(self.analysis.scalar)
453 return (length < 128 and (isinstance(self.event, AliasEvent)
454 or (isinstance(self.event, ScalarEvent)
455 and not self.analysis.empty and not self.analysis.multiline)
456 or self.check_empty_sequence() or self.check_empty_mapping()))
458 # Anchor, Tag, and Scalar processors.
460 def process_anchor(self, indicator):
461 if self.event.anchor is None:
462 self.prepared_anchor = None
464 if self.prepared_anchor is None:
465 self.prepared_anchor = self.prepare_anchor(self.event.anchor)
466 if self.prepared_anchor:
467 self.write_indicator(indicator+self.prepared_anchor, True)
468 self.prepared_anchor = None
470 def process_tag(self):
472 if isinstance(self.event, ScalarEvent):
473 if self.style is None:
474 self.style = self.choose_scalar_style()
475 if ((not self.canonical or tag is None) and
476 ((self.style == '' and self.event.implicit[0])
477 or (self.style != '' and self.event.implicit[1]))):
478 self.prepared_tag = None
480 if self.event.implicit[0] and tag is None:
482 self.prepared_tag = None
484 if (not self.canonical or tag is None) and self.event.implicit:
485 self.prepared_tag = None
488 raise EmitterError("tag is not specified")
489 if self.prepared_tag is None:
490 self.prepared_tag = self.prepare_tag(tag)
491 if self.prepared_tag:
492 self.write_indicator(self.prepared_tag, True)
493 self.prepared_tag = None
495 def choose_scalar_style(self):
496 if self.analysis is None:
497 self.analysis = self.analyze_scalar(self.event.value)
498 if self.event.style == '"' or self.canonical:
500 if not self.event.style and self.event.implicit[0]:
501 if (not (self.simple_key_context and
502 (self.analysis.empty or self.analysis.multiline))
503 and (self.flow_level and self.analysis.allow_flow_plain
504 or (not self.flow_level and self.analysis.allow_block_plain))):
506 if self.event.style and self.event.style in '|>':
507 if (not self.flow_level and not self.simple_key_context
508 and self.analysis.allow_block):
509 return self.event.style
510 if not self.event.style or self.event.style == '\'':
511 if (self.analysis.allow_single_quoted and
512 not (self.simple_key_context and self.analysis.multiline)):
516 def process_scalar(self):
517 if self.analysis is None:
518 self.analysis = self.analyze_scalar(self.event.value)
519 if self.style is None:
520 self.style = self.choose_scalar_style()
521 split = (not self.simple_key_context)
522 #if self.analysis.multiline and split \
523 # and (not self.style or self.style in '\'\"'):
524 # self.write_indent()
525 if self.style == '"':
526 self.write_double_quoted(self.analysis.scalar, split)
527 elif self.style == '\'':
528 self.write_single_quoted(self.analysis.scalar, split)
529 elif self.style == '>':
530 self.write_folded(self.analysis.scalar)
531 elif self.style == '|':
532 self.write_literal(self.analysis.scalar)
534 self.write_plain(self.analysis.scalar, split)
540 def prepare_version(self, version):
541 major, minor = version
543 raise EmitterError("unsupported YAML version: %d.%d" % (major, minor))
544 return u'%d.%d' % (major, minor)
546 def prepare_tag_handle(self, handle):
548 raise EmitterError("tag handle must not be empty")
549 if handle[0] != u'!' or handle[-1] != u'!':
550 raise EmitterError("tag handle must start and end with '!': %r"
551 % (handle.encode('utf-8')))
552 for ch in handle[1:-1]:
553 if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
555 raise EmitterError("invalid character %r in the tag handle: %r"
556 % (ch.encode('utf-8'), handle.encode('utf-8')))
559 def prepare_tag_prefix(self, prefix):
561 raise EmitterError("tag prefix must not be empty")
564 if prefix[0] == u'!':
566 while end < len(prefix):
568 if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
569 or ch in u'-;/?!:@&=+$,_.~*\'()[]':
573 chunks.append(prefix[start:end])
575 data = ch.encode('utf-8')
577 chunks.append(u'%%%02X' % ord(ch))
579 chunks.append(prefix[start:end])
580 return u''.join(chunks)
582 def prepare_tag(self, tag):
584 raise EmitterError("tag must not be empty")
589 prefixes = self.tag_prefixes.keys()
591 for prefix in prefixes:
592 if tag.startswith(prefix) \
593 and (prefix == u'!' or len(prefix) < len(tag)):
594 handle = self.tag_prefixes[prefix]
595 suffix = tag[len(prefix):]
598 while end < len(suffix):
600 if u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
601 or ch in u'-;/?:@&=+$,_.~*\'()[]' \
602 or (ch == u'!' and handle != u'!'):
606 chunks.append(suffix[start:end])
608 data = ch.encode('utf-8')
610 chunks.append(u'%%%02X' % ord(ch))
612 chunks.append(suffix[start:end])
613 suffix_text = u''.join(chunks)
615 return u'%s%s' % (handle, suffix_text)
617 return u'!<%s>' % suffix_text
619 def prepare_anchor(self, anchor):
621 raise EmitterError("anchor must not be empty")
623 if not (u'0' <= ch <= u'9' or u'A' <= ch <= u'Z' or u'a' <= ch <= u'z' \
625 raise EmitterError("invalid character %r in the anchor: %r"
626 % (ch.encode('utf-8'), anchor.encode('utf-8')))
629 def analyze_scalar(self, scalar):
631 # Empty scalar is a special case.
633 return ScalarAnalysis(scalar=scalar, empty=True, multiline=False,
634 allow_flow_plain=False, allow_block_plain=True,
635 allow_single_quoted=True, allow_double_quoted=True,
638 # Indicators and special characters.
639 block_indicators = False
640 flow_indicators = False
642 special_characters = False
644 # Important whitespace combinations.
645 leading_space = False
646 leading_break = False
647 trailing_space = False
648 trailing_break = False
652 # Check document indicators.
653 if scalar.startswith(u'---') or scalar.startswith(u'...'):
654 block_indicators = True
655 flow_indicators = True
657 # First character or preceded by a whitespace.
658 preceeded_by_whitespace = True
660 # Last character or followed by a whitespace.
661 followed_by_whitespace = (len(scalar) == 1 or
662 scalar[1] in u'\0 \t\r\n\x85\u2028\u2029')
664 # The previous character is a space.
665 previous_space = False
667 # The previous character is a break.
668 previous_break = False
671 while index < len(scalar):
674 # Check for indicators.
676 # Leading indicators are special characters.
677 if ch in u'#,[]{}&*!|>\'\"%@`':
678 flow_indicators = True
679 block_indicators = True
681 flow_indicators = True
682 if followed_by_whitespace:
683 block_indicators = True
684 if ch == u'-' and followed_by_whitespace:
685 flow_indicators = True
686 block_indicators = True
688 # Some indicators cannot appear within a scalar as well.
690 flow_indicators = True
692 flow_indicators = True
693 if followed_by_whitespace:
694 block_indicators = True
695 if ch == u'#' and preceeded_by_whitespace:
696 flow_indicators = True
697 block_indicators = True
699 # Check for line breaks, special, and unicode characters.
700 if ch in u'\n\x85\u2028\u2029':
702 if not (ch == u'\n' or u'\x20' <= ch <= u'\x7E'):
703 if (ch == u'\x85' or u'\xA0' <= ch <= u'\uD7FF'
704 or u'\uE000' <= ch <= u'\uFFFD') and ch != u'\uFEFF':
705 unicode_characters = True
706 if not self.allow_unicode:
707 special_characters = True
709 special_characters = True
711 # Detect important whitespace combinations.
715 if index == len(scalar)-1:
716 trailing_space = True
719 previous_space = True
720 previous_break = False
721 elif ch in u'\n\x85\u2028\u2029':
724 if index == len(scalar)-1:
725 trailing_break = True
728 previous_space = False
729 previous_break = True
731 previous_space = False
732 previous_break = False
734 # Prepare for the next character.
736 preceeded_by_whitespace = (ch in u'\0 \t\r\n\x85\u2028\u2029')
737 followed_by_whitespace = (index+1 >= len(scalar) or
738 scalar[index+1] in u'\0 \t\r\n\x85\u2028\u2029')
740 # Let's decide what styles are allowed.
741 allow_flow_plain = True
742 allow_block_plain = True
743 allow_single_quoted = True
744 allow_double_quoted = True
747 # Leading and trailing whitespaces are bad for plain scalars.
748 if (leading_space or leading_break
749 or trailing_space or trailing_break):
750 allow_flow_plain = allow_block_plain = False
752 # We do not permit trailing spaces for block scalars.
756 # Spaces at the beginning of a new line are only acceptable for block
759 allow_flow_plain = allow_block_plain = allow_single_quoted = False
761 # Spaces followed by breaks, as well as special character are only
762 # allowed for double quoted scalars.
763 if space_break or special_characters:
764 allow_flow_plain = allow_block_plain = \
765 allow_single_quoted = allow_block = False
767 # Although the plain scalar writer supports breaks, we never emit
768 # multiline plain scalars.
770 allow_flow_plain = allow_block_plain = False
772 # Flow indicators are forbidden for flow plain scalars.
774 allow_flow_plain = False
776 # Block indicators are forbidden for block plain scalars.
778 allow_block_plain = False
780 return ScalarAnalysis(scalar=scalar,
781 empty=False, multiline=line_breaks,
782 allow_flow_plain=allow_flow_plain,
783 allow_block_plain=allow_block_plain,
784 allow_single_quoted=allow_single_quoted,
785 allow_double_quoted=allow_double_quoted,
786 allow_block=allow_block)
790 def flush_stream(self):
791 if hasattr(self.stream, 'flush'):
794 def write_stream_start(self):
795 # Write BOM if needed.
796 if self.encoding and self.encoding.startswith('utf-16'):
797 self.stream.write(u'\uFEFF'.encode(self.encoding))
799 def write_stream_end(self):
802 def write_indicator(self, indicator, need_whitespace,
803 whitespace=False, indention=False):
804 if self.whitespace or not need_whitespace:
807 data = u' '+indicator
808 self.whitespace = whitespace
809 self.indention = self.indention and indention
810 self.column += len(data)
811 self.open_ended = False
813 data = data.encode(self.encoding)
814 self.stream.write(data)
816 def write_indent(self):
817 indent = self.indent or 0
818 if not self.indention or self.column > indent \
819 or (self.column == indent and not self.whitespace):
820 self.write_line_break()
821 if self.column < indent:
822 self.whitespace = True
823 data = u' '*(indent-self.column)
826 data = data.encode(self.encoding)
827 self.stream.write(data)
829 def write_line_break(self, data=None):
831 data = self.best_line_break
832 self.whitespace = True
833 self.indention = True
837 data = data.encode(self.encoding)
838 self.stream.write(data)
840 def write_version_directive(self, version_text):
841 data = u'%%YAML %s' % version_text
843 data = data.encode(self.encoding)
844 self.stream.write(data)
845 self.write_line_break()
847 def write_tag_directive(self, handle_text, prefix_text):
848 data = u'%%TAG %s %s' % (handle_text, prefix_text)
850 data = data.encode(self.encoding)
851 self.stream.write(data)
852 self.write_line_break()
856 def write_single_quoted(self, text, split=True):
857 self.write_indicator(u'\'', True)
861 while end <= len(text):
866 if ch is None or ch != u' ':
867 if start+1 == end and self.column > self.best_width and split \
868 and start != 0 and end != len(text):
871 data = text[start:end]
872 self.column += len(data)
874 data = data.encode(self.encoding)
875 self.stream.write(data)
878 if ch is None or ch not in u'\n\x85\u2028\u2029':
879 if text[start] == u'\n':
880 self.write_line_break()
881 for br in text[start:end]:
883 self.write_line_break()
885 self.write_line_break(br)
889 if ch is None or ch in u' \n\x85\u2028\u2029' or ch == u'\'':
891 data = text[start:end]
892 self.column += len(data)
894 data = data.encode(self.encoding)
895 self.stream.write(data)
901 data = data.encode(self.encoding)
902 self.stream.write(data)
905 spaces = (ch == u' ')
906 breaks = (ch in u'\n\x85\u2028\u2029')
908 self.write_indicator(u'\'', False)
910 ESCAPE_REPLACEMENTS = {
928 def write_double_quoted(self, text, split=True):
929 self.write_indicator(u'"', True)
931 while end <= len(text):
935 if ch is None or ch in u'"\\\x85\u2028\u2029\uFEFF' \
936 or not (u'\x20' <= ch <= u'\x7E'
937 or (self.allow_unicode
938 and (u'\xA0' <= ch <= u'\uD7FF'
939 or u'\uE000' <= ch <= u'\uFFFD'))):
941 data = text[start:end]
942 self.column += len(data)
944 data = data.encode(self.encoding)
945 self.stream.write(data)
948 if ch in self.ESCAPE_REPLACEMENTS:
949 data = u'\\'+self.ESCAPE_REPLACEMENTS[ch]
951 data = u'\\x%02X' % ord(ch)
952 elif ch <= u'\uFFFF':
953 data = u'\\u%04X' % ord(ch)
955 data = u'\\U%08X' % ord(ch)
956 self.column += len(data)
958 data = data.encode(self.encoding)
959 self.stream.write(data)
961 if 0 < end < len(text)-1 and (ch == u' ' or start >= end) \
962 and self.column+(end-start) > self.best_width and split:
963 data = text[start:end]+u'\\'
966 self.column += len(data)
968 data = data.encode(self.encoding)
969 self.stream.write(data)
971 self.whitespace = False
972 self.indention = False
973 if text[start] == u' ':
975 self.column += len(data)
977 data = data.encode(self.encoding)
978 self.stream.write(data)
980 self.write_indicator(u'"', False)
982 def determine_block_hints(self, text):
985 if text[0] in u' \n\x85\u2028\u2029':
986 hints += unicode(self.best_indent)
987 if text[-1] not in u'\n\x85\u2028\u2029':
989 elif len(text) == 1 or text[-2] in u'\n\x85\u2028\u2029':
993 def write_folded(self, text):
994 hints = self.determine_block_hints(text)
995 self.write_indicator(u'>'+hints, True)
996 if hints[-1:] == u'+':
997 self.open_ended = True
998 self.write_line_break()
1003 while end <= len(text):
1008 if ch is None or ch not in u'\n\x85\u2028\u2029':
1009 if not leading_space and ch is not None and ch != u' ' \
1010 and text[start] == u'\n':
1011 self.write_line_break()
1012 leading_space = (ch == u' ')
1013 for br in text[start:end]:
1015 self.write_line_break()
1017 self.write_line_break(br)
1023 if start+1 == end and self.column > self.best_width:
1026 data = text[start:end]
1027 self.column += len(data)
1029 data = data.encode(self.encoding)
1030 self.stream.write(data)
1033 if ch is None or ch in u' \n\x85\u2028\u2029':
1034 data = text[start:end]
1035 self.column += len(data)
1037 data = data.encode(self.encoding)
1038 self.stream.write(data)
1040 self.write_line_break()
1043 breaks = (ch in u'\n\x85\u2028\u2029')
1044 spaces = (ch == u' ')
1047 def write_literal(self, text):
1048 hints = self.determine_block_hints(text)
1049 self.write_indicator(u'|'+hints, True)
1050 if hints[-1:] == u'+':
1051 self.open_ended = True
1052 self.write_line_break()
1055 while end <= len(text):
1060 if ch is None or ch not in u'\n\x85\u2028\u2029':
1061 for br in text[start:end]:
1063 self.write_line_break()
1065 self.write_line_break(br)
1070 if ch is None or ch in u'\n\x85\u2028\u2029':
1071 data = text[start:end]
1073 data = data.encode(self.encoding)
1074 self.stream.write(data)
1076 self.write_line_break()
1079 breaks = (ch in u'\n\x85\u2028\u2029')
1082 def write_plain(self, text, split=True):
1083 if self.root_context:
1084 self.open_ended = True
1087 if not self.whitespace:
1089 self.column += len(data)
1091 data = data.encode(self.encoding)
1092 self.stream.write(data)
1093 self.whitespace = False
1094 self.indention = False
1098 while end <= len(text):
1104 if start+1 == end and self.column > self.best_width and split:
1106 self.whitespace = False
1107 self.indention = False
1109 data = text[start:end]
1110 self.column += len(data)
1112 data = data.encode(self.encoding)
1113 self.stream.write(data)
1116 if ch not in u'\n\x85\u2028\u2029':
1117 if text[start] == u'\n':
1118 self.write_line_break()
1119 for br in text[start:end]:
1121 self.write_line_break()
1123 self.write_line_break(br)
1125 self.whitespace = False
1126 self.indention = False
1129 if ch is None or ch in u' \n\x85\u2028\u2029':
1130 data = text[start:end]
1131 self.column += len(data)
1133 data = data.encode(self.encoding)
1134 self.stream.write(data)
1137 spaces = (ch == u' ')
1138 breaks = (ch in u'\n\x85\u2028\u2029')