from __future__ import absolute_import,print_function _T='\ufeff' _S='\ue000' _R='\ud7ff' _Q='%%%02X' _P='version' _O="-;/?:@&=+$,_.~*'()[]" _N=' \n\x85\u2028\u2029' _M='\xa0' _L='...' _K='\\' _J="'" _I='?' _H='"' _G='!' _F='\n\x85\u2028\u2029' _E='\n' _D=' ' _C=False _B=None _A=True import sys from.error import YAMLError,YAMLStreamError from.events import* from.compat import utf8,text_type,PY2,nprint,dbg,DBG_EVENT,check_anchorname_char if _C:from typing import Any,Dict,List,Union,Text,Tuple,Optional;from.compat import StreamType __all__=['Emitter','EmitterError'] class EmitterError(YAMLError):0 class ScalarAnalysis: def __init__(self,scalar,empty,multiline,allow_flow_plain,allow_block_plain,allow_single_quoted,allow_double_quoted,allow_block):self.scalar=scalar;self.empty=empty;self.multiline=multiline;self.allow_flow_plain=allow_flow_plain;self.allow_block_plain=allow_block_plain;self.allow_single_quoted=allow_single_quoted;self.allow_double_quoted=allow_double_quoted;self.allow_block=allow_block class Indents: def __init__(self):self.values=[] def append(self,val,seq):self.values.append((val,seq)) def pop(self):return self.values.pop()[0] def last_seq(self): try:return self.values[-2][1] except IndexError:return _C def seq_flow_align(self,seq_indent,column): if len(self.values)<2 or not self.values[-1][1]:return 0 base=self.values[-1][0]if self.values[-1][0]is not _B else 0;return base+seq_indent-column-1 def __len__(self):return len(self.values) class Emitter: DEFAULT_TAG_PREFIXES={_G:_G,'tag:yaml.org,2002:':'!!'};MAX_SIMPLE_KEY_LENGTH=128 def __init__(self,stream,canonical=_B,indent=_B,width=_B,allow_unicode=_B,line_break=_B,block_seq_indent=_B,top_level_colon_align=_B,prefix_colon=_B,brace_single_entry_mapping_in_flow_sequence=_B,dumper=_B): self.dumper=dumper if self.dumper is not _B and getattr(self.dumper,'_emitter',_B)is _B:self.dumper._emitter=self self.stream=stream;self.encoding=_B;self.allow_space_break=_B;self.states=[];self.state=self.expect_stream_start;self.events=[];self.event=_B;self.indents=Indents();self.indent=_B;self.flow_context=[];self.root_context=_C;self.sequence_context=_C;self.mapping_context=_C;self.simple_key_context=_C;self.line=0;self.column=0;self.whitespace=_A;self.indention=_A;self.compact_seq_seq=_A;self.compact_seq_map=_A;self.no_newline=_B;self.open_ended=_C;self.colon=':';self.prefixed_colon=self.colon if prefix_colon is _B else prefix_colon+self.colon;self.brace_single_entry_mapping_in_flow_sequence=brace_single_entry_mapping_in_flow_sequence;self.canonical=canonical;self.allow_unicode=allow_unicode;self.unicode_supplementary=sys.maxunicode>65535;self.sequence_dash_offset=block_seq_indent if block_seq_indent else 0;self.top_level_colon_align=top_level_colon_align;self.best_sequence_indent=2;self.requested_indent=indent if indent and 1self.best_sequence_indent*2:self.best_width=width self.best_line_break=_E if line_break in['\r',_E,'\r\n']:self.best_line_break=line_break self.tag_prefixes=_B;self.prepared_anchor=_B;self.prepared_tag=_B;self.analysis=_B;self.style=_B;self.scalar_after_indicator=_A @property def stream(self): try:return self._stream except AttributeError:raise YAMLStreamError('output stream needs to specified') @stream.setter def stream(self,val): if val is _B:return if not hasattr(val,'write'):raise YAMLStreamError('stream argument needs to have a write() method') self._stream=val @property def serializer(self): try: if hasattr(self.dumper,'typ'):return self.dumper.serializer return self.dumper._serializer except AttributeError:return self @property def flow_level(self):return len(self.flow_context) def dispose(self):self.states=[];self.state=_B def emit(self,event): if dbg(DBG_EVENT):nprint(event) self.events.append(event) while not self.need_more_events():self.event=self.events.pop(0);self.state();self.event=_B def need_more_events(self): if not self.events:return _A event=self.events[0] if isinstance(event,DocumentStartEvent):return self.need_events(1) elif isinstance(event,SequenceStartEvent):return self.need_events(2) elif isinstance(event,MappingStartEvent):return self.need_events(3) else:return _C def need_events(self,count): level=0 for event in self.events[1:]: if isinstance(event,(DocumentStartEvent,CollectionStartEvent)):level+=1 elif isinstance(event,(DocumentEndEvent,CollectionEndEvent)):level-=1 elif isinstance(event,StreamEndEvent):level=-1 if level<0:return _C return len(self.events)self.best_width:self.write_indent() self.states.append(self.expect_flow_sequence_item);self.expect_node(sequence=_A) def expect_flow_sequence_item(self): if isinstance(self.event,SequenceEndEvent): self.indent=self.indents.pop();popped=self.flow_context.pop();assert popped=='[' if self.canonical:self.write_indicator(',',_C);self.write_indent() self.write_indicator(']',_C) if self.event.comment and self.event.comment[0]:self.write_post_comment(self.event) else:self.no_newline=_C self.state=self.states.pop() else: self.write_indicator(',',_C) if self.canonical or self.column>self.best_width:self.write_indent() self.states.append(self.expect_flow_sequence_item);self.expect_node(sequence=_A) def expect_flow_mapping(self,single=_C): ind=self.indents.seq_flow_align(self.best_sequence_indent,self.column);map_init='{' if single and self.flow_level and self.flow_context[-1]=='['and not self.canonical and not self.brace_single_entry_mapping_in_flow_sequence:map_init='' self.write_indicator(_D*ind+map_init,_A,whitespace=_A);self.flow_context.append(map_init);self.increase_indent(flow=_A,sequence=_C);self.state=self.expect_first_flow_mapping_key def expect_first_flow_mapping_key(self): if isinstance(self.event,MappingEndEvent): self.indent=self.indents.pop();popped=self.flow_context.pop();assert popped=='{';self.write_indicator('}',_C) if self.event.comment and self.event.comment[0]:self.write_post_comment(self.event) elif self.flow_level==0:self.write_line_break() self.state=self.states.pop() else: if self.canonical or self.column>self.best_width:self.write_indent() if not self.canonical and self.check_simple_key():self.states.append(self.expect_flow_mapping_simple_value);self.expect_node(mapping=_A,simple_key=_A) else:self.write_indicator(_I,_A);self.states.append(self.expect_flow_mapping_value);self.expect_node(mapping=_A) def expect_flow_mapping_key(self): if isinstance(self.event,MappingEndEvent): self.indent=self.indents.pop();popped=self.flow_context.pop();assert popped in['{',''] if self.canonical:self.write_indicator(',',_C);self.write_indent() if popped!='':self.write_indicator('}',_C) if self.event.comment and self.event.comment[0]:self.write_post_comment(self.event) else:self.no_newline=_C self.state=self.states.pop() else: self.write_indicator(',',_C) if self.canonical or self.column>self.best_width:self.write_indent() if not self.canonical and self.check_simple_key():self.states.append(self.expect_flow_mapping_simple_value);self.expect_node(mapping=_A,simple_key=_A) else:self.write_indicator(_I,_A);self.states.append(self.expect_flow_mapping_value);self.expect_node(mapping=_A) def expect_flow_mapping_simple_value(self):self.write_indicator(self.prefixed_colon,_C);self.states.append(self.expect_flow_mapping_key);self.expect_node(mapping=_A) def expect_flow_mapping_value(self): if self.canonical or self.column>self.best_width:self.write_indent() self.write_indicator(self.prefixed_colon,_A);self.states.append(self.expect_flow_mapping_key);self.expect_node(mapping=_A) def expect_block_sequence(self): if self.mapping_context:indentless=not self.indention else: indentless=_C if not self.compact_seq_seq and self.column!=0:self.write_line_break() self.increase_indent(flow=_C,sequence=_A,indentless=indentless);self.state=self.expect_first_block_sequence_item def expect_first_block_sequence_item(self):return self.expect_block_sequence_item(first=_A) def expect_block_sequence_item(self,first=_C): if not first and isinstance(self.event,SequenceEndEvent): if self.event.comment and self.event.comment[1]:self.write_pre_comment(self.event) self.indent=self.indents.pop();self.state=self.states.pop();self.no_newline=_C else: if self.event.comment and self.event.comment[1]:self.write_pre_comment(self.event) nonl=self.no_newline if self.column==0 else _C;self.write_indent();ind=self.sequence_dash_offset;self.write_indicator(_D*ind+'-',_A,indention=_A) if nonl or self.sequence_dash_offset+2>self.best_sequence_indent:self.no_newline=_A self.states.append(self.expect_block_sequence_item);self.expect_node(sequence=_A) def expect_block_mapping(self): if not self.mapping_context and not(self.compact_seq_map or self.column==0):self.write_line_break() self.increase_indent(flow=_C,sequence=_C);self.state=self.expect_first_block_mapping_key def expect_first_block_mapping_key(self):return self.expect_block_mapping_key(first=_A) def expect_block_mapping_key(self,first=_C): if not first and isinstance(self.event,MappingEndEvent): if self.event.comment and self.event.comment[1]:self.write_pre_comment(self.event) self.indent=self.indents.pop();self.state=self.states.pop() else: if self.event.comment and self.event.comment[1]:self.write_pre_comment(self.event) self.write_indent() if self.check_simple_key(): if not isinstance(self.event,(SequenceStartEvent,MappingStartEvent)): try: if self.event.style==_I:self.write_indicator(_I,_A,indention=_A) except AttributeError:pass self.states.append(self.expect_block_mapping_simple_value);self.expect_node(mapping=_A,simple_key=_A) if isinstance(self.event,AliasEvent):self.stream.write(_D) else:self.write_indicator(_I,_A,indention=_A);self.states.append(self.expect_block_mapping_value);self.expect_node(mapping=_A) def expect_block_mapping_simple_value(self): if getattr(self.event,'style',_B)!=_I: if self.indent==0 and self.top_level_colon_align is not _B:c=_D*(self.top_level_colon_align-self.column)+self.colon else:c=self.prefixed_colon self.write_indicator(c,_C) self.states.append(self.expect_block_mapping_key);self.expect_node(mapping=_A) def expect_block_mapping_value(self):self.write_indent();self.write_indicator(self.prefixed_colon,_A,indention=_A);self.states.append(self.expect_block_mapping_key);self.expect_node(mapping=_A) def check_empty_sequence(self):return isinstance(self.event,SequenceStartEvent)and bool(self.events)and isinstance(self.events[0],SequenceEndEvent) def check_empty_mapping(self):return isinstance(self.event,MappingStartEvent)and bool(self.events)and isinstance(self.events[0],MappingEndEvent) def check_empty_document(self): if not isinstance(self.event,DocumentStartEvent)or not self.events:return _C event=self.events[0];return isinstance(event,ScalarEvent)and event.anchor is _B and event.tag is _B and event.implicit and event.value=='' def check_simple_key(self): length=0 if isinstance(self.event,NodeEvent)and self.event.anchor is not _B: if self.prepared_anchor is _B:self.prepared_anchor=self.prepare_anchor(self.event.anchor) length+=len(self.prepared_anchor) if isinstance(self.event,(ScalarEvent,CollectionStartEvent))and self.event.tag is not _B: if self.prepared_tag is _B:self.prepared_tag=self.prepare_tag(self.event.tag) length+=len(self.prepared_tag) if isinstance(self.event,ScalarEvent): if self.analysis is _B:self.analysis=self.analyze_scalar(self.event.value) length+=len(self.analysis.scalar) return length': if not self.flow_level and not self.simple_key_context and self.analysis.allow_block:return self.event.style if not self.event.style and self.analysis.allow_double_quoted: if _J in self.event.value or _E in self.event.value:return _H if not self.event.style or self.event.style==_J: if self.analysis.allow_single_quoted and not(self.simple_key_context and self.analysis.multiline):return _J return _H def process_scalar(self): if self.analysis is _B:self.analysis=self.analyze_scalar(self.event.value) if self.style is _B:self.style=self.choose_scalar_style() split=not self.simple_key_context if self.sequence_context and not self.flow_level:self.write_indent() if self.style==_H:self.write_double_quoted(self.analysis.scalar,split) elif self.style==_J:self.write_single_quoted(self.analysis.scalar,split) elif self.style=='>':self.write_folded(self.analysis.scalar) elif self.style=='|':self.write_literal(self.analysis.scalar,self.event.comment) else:self.write_plain(self.analysis.scalar,split) self.analysis=_B;self.style=_B if self.event.comment:self.write_post_comment(self.event) def prepare_version(self,version): major,minor=version if major!=1:raise EmitterError('unsupported YAML version: %d.%d'%(major,minor)) return'%d.%d'%(major,minor) def prepare_tag_handle(self,handle): if not handle:raise EmitterError('tag handle must not be empty') if handle[0]!=_G or handle[-1]!=_G:raise EmitterError("tag handle must start and end with '!': %r"%utf8(handle)) for ch in handle[1:-1]: if not('0'<=ch<='9'or'A'<=ch<='Z'or'a'<=ch<='z'or ch in'-_'):raise EmitterError('invalid character %r in the tag handle: %r'%(utf8(ch),utf8(handle))) return handle def prepare_tag_prefix(self,prefix): if not prefix:raise EmitterError('tag prefix must not be empty') chunks=[];start=end=0 if prefix[0]==_G:end=1 ch_set=_O if self.dumper: version=getattr(self.dumper,_P,(1,2)) if version is _B or version>=(1,2):ch_set+='#' while end=(1,2):ch_set+='#' while end'%suffix_text def prepare_anchor(self,anchor): if not anchor:raise EmitterError('anchor must not be empty') for ch in anchor: if not check_anchorname_char(ch):raise EmitterError('invalid character %r in the anchor: %r'%(utf8(ch),utf8(anchor))) return anchor def analyze_scalar(self,scalar): A='\x00 \t\r\n\x85\u2028\u2029' if not scalar:return ScalarAnalysis(scalar=scalar,empty=_A,multiline=_C,allow_flow_plain=_C,allow_block_plain=_A,allow_single_quoted=_A,allow_double_quoted=_A,allow_block=_C) block_indicators=_C;flow_indicators=_C;line_breaks=_C;special_characters=_C;leading_space=_C;leading_break=_C;trailing_space=_C;trailing_break=_C;break_space=_C;space_break=_C if scalar.startswith('---')or scalar.startswith(_L):block_indicators=_A;flow_indicators=_A preceeded_by_whitespace=_A;followed_by_whitespace=len(scalar)==1 or scalar[1]in A;previous_space=_C;previous_break=_C;index=0 while index\'"%@`':flow_indicators=_A;block_indicators=_A if ch in'?:': if self.serializer.use_version==(1,1):flow_indicators=_A elif len(scalar)==1:flow_indicators=_A if followed_by_whitespace:block_indicators=_A if ch=='-'and followed_by_whitespace:flow_indicators=_A;block_indicators=_A else: if ch in',[]{}':flow_indicators=_A if ch==_I and self.serializer.use_version==(1,1):flow_indicators=_A if ch==':': if followed_by_whitespace:flow_indicators=_A;block_indicators=_A if ch=='#'and preceeded_by_whitespace:flow_indicators=_A;block_indicators=_A if ch in _F:line_breaks=_A if not(ch==_E or _D<=ch<='~'): if(ch=='\x85'or _M<=ch<=_R or _S<=ch<='�'or self.unicode_supplementary and'𐀀'<=ch<='\U0010ffff')and ch!=_T: if not self.allow_unicode:special_characters=_A else:special_characters=_A if ch==_D: if index==0:leading_space=_A if index==len(scalar)-1:trailing_space=_A if previous_break:break_space=_A previous_space=_A;previous_break=_C elif ch in _F: if index==0:leading_break=_A if index==len(scalar)-1:trailing_break=_A if previous_space:space_break=_A previous_space=_C;previous_break=_A else:previous_space=_C;previous_break=_C index+=1;preceeded_by_whitespace=ch in A;followed_by_whitespace=index+1>=len(scalar)or scalar[index+1]in A allow_flow_plain=_A;allow_block_plain=_A;allow_single_quoted=_A;allow_double_quoted=_A;allow_block=_A if leading_space or leading_break or trailing_space or trailing_break:allow_flow_plain=allow_block_plain=_C if trailing_space:allow_block=_C if break_space:allow_flow_plain=allow_block_plain=allow_single_quoted=_C if special_characters:allow_flow_plain=allow_block_plain=allow_single_quoted=allow_block=_C elif space_break: allow_flow_plain=allow_block_plain=allow_single_quoted=_C if not self.allow_space_break:allow_block=_C if line_breaks:allow_flow_plain=allow_block_plain=_C if flow_indicators:allow_flow_plain=_C if block_indicators:allow_block_plain=_C return ScalarAnalysis(scalar=scalar,empty=_C,multiline=line_breaks,allow_flow_plain=allow_flow_plain,allow_block_plain=allow_block_plain,allow_single_quoted=allow_single_quoted,allow_double_quoted=allow_double_quoted,allow_block=allow_block) def flush_stream(self): if hasattr(self.stream,'flush'):self.stream.flush() def write_stream_start(self): if self.encoding and self.encoding.startswith('utf-16'):self.stream.write(_T.encode(self.encoding)) def write_stream_end(self):self.flush_stream() def write_indicator(self,indicator,need_whitespace,whitespace=_C,indention=_C): if self.whitespace or not need_whitespace:data=indicator else:data=_D+indicator self.whitespace=whitespace;self.indention=self.indention and indention;self.column+=len(data);self.open_ended=_C if bool(self.encoding):data=data.encode(self.encoding) self.stream.write(data) def write_indent(self): indent=self.indent or 0 if not self.indention or self.column>indent or self.column==indent and not self.whitespace: if bool(self.no_newline):self.no_newline=_C else:self.write_line_break() if self.columnself.best_width and split and start!=0 and end!=len(text):self.write_indent() else: data=text[start:end];self.column+=len(data) if bool(self.encoding):data=data.encode(self.encoding) self.stream.write(data) start=end elif breaks: if ch is _B or ch not in _F: if text[start]==_E:self.write_line_break() for br in text[start:end]: if br==_E:self.write_line_break() else:self.write_line_break(br) self.write_indent();start=end elif ch is _B or ch in _N or ch==_J: if start=end)and self.column+(end-start)>self.best_width and split: data=text[start:end]+_K if start-1:break if pos>0:indent=self.best_sequence_indent if text[-1]not in _F:indicator='-' elif len(text)==1 or text[-2]in _F:indicator='+' hints+=indicator;return hints,indent,indicator def write_folded(self,text): hints,_indent,_indicator=self.determine_block_hints(text);self.write_indicator('>'+hints,_A) if _indicator=='+':self.open_ended=_A self.write_line_break();leading_space=_A;spaces=_C;breaks=_A;start=end=0 while end<=len(text): ch=_B if endself.best_width:self.write_indent() else: data=text[start:end];self.column+=len(data) if bool(self.encoding):data=data.encode(self.encoding) self.stream.write(data) start=end elif ch is _B or ch in' \n\x85\u2028\u2029\x07': data=text[start:end];self.column+=len(data) if bool(self.encoding):data=data.encode(self.encoding) self.stream.write(data) if ch=='\x07': if endself.best_width and split:self.write_indent();self.whitespace=_C;self.indention=_C else: data=text[start:end];self.column+=len(data) if self.encoding:data=data.encode(self.encoding) self.stream.write(data) start=end elif breaks: if ch not in _F: if text[start]==_E:self.write_line_break() for br in text[start:end]: if br==_E:self.write_line_break() else:self.write_line_break(br) self.write_indent();self.whitespace=_C;self.indention=_C;start=end elif ch is _B or ch in _N: data=text[start:end];self.column+=len(data) if self.encoding:data=data.encode(self.encoding) try:self.stream.write(data) except:sys.stdout.write(repr(data)+_E);raise start=end if ch is not _B:spaces=ch==_D;breaks=ch in _F end+=1 def write_comment(self,comment,pre=_C): value=comment.value if not pre and value[-1]==_E:value=value[:-1] try: col=comment.start_mark.column if comment.value and comment.value.startswith(_E):col=self.column elif col