1a5f9918aSopenharmony_ci 2a5f9918aSopenharmony_ciimport yaml, yaml.composer, yaml.constructor, yaml.resolver 3a5f9918aSopenharmony_ci 4a5f9918aSopenharmony_ciclass CanonicalError(yaml.YAMLError): 5a5f9918aSopenharmony_ci pass 6a5f9918aSopenharmony_ci 7a5f9918aSopenharmony_ciclass CanonicalScanner: 8a5f9918aSopenharmony_ci 9a5f9918aSopenharmony_ci def __init__(self, data): 10a5f9918aSopenharmony_ci if isinstance(data, bytes): 11a5f9918aSopenharmony_ci try: 12a5f9918aSopenharmony_ci data = data.decode('utf-8') 13a5f9918aSopenharmony_ci except UnicodeDecodeError: 14a5f9918aSopenharmony_ci raise CanonicalError("utf-8 stream is expected") 15a5f9918aSopenharmony_ci self.data = data+'\0' 16a5f9918aSopenharmony_ci self.index = 0 17a5f9918aSopenharmony_ci self.tokens = [] 18a5f9918aSopenharmony_ci self.scanned = False 19a5f9918aSopenharmony_ci 20a5f9918aSopenharmony_ci def check_token(self, *choices): 21a5f9918aSopenharmony_ci if not self.scanned: 22a5f9918aSopenharmony_ci self.scan() 23a5f9918aSopenharmony_ci if self.tokens: 24a5f9918aSopenharmony_ci if not choices: 25a5f9918aSopenharmony_ci return True 26a5f9918aSopenharmony_ci for choice in choices: 27a5f9918aSopenharmony_ci if isinstance(self.tokens[0], choice): 28a5f9918aSopenharmony_ci return True 29a5f9918aSopenharmony_ci return False 30a5f9918aSopenharmony_ci 31a5f9918aSopenharmony_ci def peek_token(self): 32a5f9918aSopenharmony_ci if not self.scanned: 33a5f9918aSopenharmony_ci self.scan() 34a5f9918aSopenharmony_ci if self.tokens: 35a5f9918aSopenharmony_ci return self.tokens[0] 36a5f9918aSopenharmony_ci 37a5f9918aSopenharmony_ci def get_token(self, choice=None): 38a5f9918aSopenharmony_ci if not self.scanned: 39a5f9918aSopenharmony_ci self.scan() 40a5f9918aSopenharmony_ci token = self.tokens.pop(0) 41a5f9918aSopenharmony_ci if choice and not isinstance(token, choice): 42a5f9918aSopenharmony_ci raise CanonicalError("unexpected token "+repr(token)) 43a5f9918aSopenharmony_ci return token 44a5f9918aSopenharmony_ci 45a5f9918aSopenharmony_ci def get_token_value(self): 46a5f9918aSopenharmony_ci token = self.get_token() 47a5f9918aSopenharmony_ci return token.value 48a5f9918aSopenharmony_ci 49a5f9918aSopenharmony_ci def scan(self): 50a5f9918aSopenharmony_ci self.tokens.append(yaml.StreamStartToken(None, None)) 51a5f9918aSopenharmony_ci while True: 52a5f9918aSopenharmony_ci self.find_token() 53a5f9918aSopenharmony_ci ch = self.data[self.index] 54a5f9918aSopenharmony_ci if ch == '\0': 55a5f9918aSopenharmony_ci self.tokens.append(yaml.StreamEndToken(None, None)) 56a5f9918aSopenharmony_ci break 57a5f9918aSopenharmony_ci elif ch == '%': 58a5f9918aSopenharmony_ci self.tokens.append(self.scan_directive()) 59a5f9918aSopenharmony_ci elif ch == '-' and self.data[self.index:self.index+3] == '---': 60a5f9918aSopenharmony_ci self.index += 3 61a5f9918aSopenharmony_ci self.tokens.append(yaml.DocumentStartToken(None, None)) 62a5f9918aSopenharmony_ci elif ch == '[': 63a5f9918aSopenharmony_ci self.index += 1 64a5f9918aSopenharmony_ci self.tokens.append(yaml.FlowSequenceStartToken(None, None)) 65a5f9918aSopenharmony_ci elif ch == '{': 66a5f9918aSopenharmony_ci self.index += 1 67a5f9918aSopenharmony_ci self.tokens.append(yaml.FlowMappingStartToken(None, None)) 68a5f9918aSopenharmony_ci elif ch == ']': 69a5f9918aSopenharmony_ci self.index += 1 70a5f9918aSopenharmony_ci self.tokens.append(yaml.FlowSequenceEndToken(None, None)) 71a5f9918aSopenharmony_ci elif ch == '}': 72a5f9918aSopenharmony_ci self.index += 1 73a5f9918aSopenharmony_ci self.tokens.append(yaml.FlowMappingEndToken(None, None)) 74a5f9918aSopenharmony_ci elif ch == '?': 75a5f9918aSopenharmony_ci self.index += 1 76a5f9918aSopenharmony_ci self.tokens.append(yaml.KeyToken(None, None)) 77a5f9918aSopenharmony_ci elif ch == ':': 78a5f9918aSopenharmony_ci self.index += 1 79a5f9918aSopenharmony_ci self.tokens.append(yaml.ValueToken(None, None)) 80a5f9918aSopenharmony_ci elif ch == ',': 81a5f9918aSopenharmony_ci self.index += 1 82a5f9918aSopenharmony_ci self.tokens.append(yaml.FlowEntryToken(None, None)) 83a5f9918aSopenharmony_ci elif ch == '*' or ch == '&': 84a5f9918aSopenharmony_ci self.tokens.append(self.scan_alias()) 85a5f9918aSopenharmony_ci elif ch == '!': 86a5f9918aSopenharmony_ci self.tokens.append(self.scan_tag()) 87a5f9918aSopenharmony_ci elif ch == '"': 88a5f9918aSopenharmony_ci self.tokens.append(self.scan_scalar()) 89a5f9918aSopenharmony_ci else: 90a5f9918aSopenharmony_ci raise CanonicalError("invalid token") 91a5f9918aSopenharmony_ci self.scanned = True 92a5f9918aSopenharmony_ci 93a5f9918aSopenharmony_ci DIRECTIVE = '%YAML 1.1' 94a5f9918aSopenharmony_ci 95a5f9918aSopenharmony_ci def scan_directive(self): 96a5f9918aSopenharmony_ci if self.data[self.index:self.index+len(self.DIRECTIVE)] == self.DIRECTIVE and \ 97a5f9918aSopenharmony_ci self.data[self.index+len(self.DIRECTIVE)] in ' \n\0': 98a5f9918aSopenharmony_ci self.index += len(self.DIRECTIVE) 99a5f9918aSopenharmony_ci return yaml.DirectiveToken('YAML', (1, 1), None, None) 100a5f9918aSopenharmony_ci else: 101a5f9918aSopenharmony_ci raise CanonicalError("invalid directive") 102a5f9918aSopenharmony_ci 103a5f9918aSopenharmony_ci def scan_alias(self): 104a5f9918aSopenharmony_ci if self.data[self.index] == '*': 105a5f9918aSopenharmony_ci TokenClass = yaml.AliasToken 106a5f9918aSopenharmony_ci else: 107a5f9918aSopenharmony_ci TokenClass = yaml.AnchorToken 108a5f9918aSopenharmony_ci self.index += 1 109a5f9918aSopenharmony_ci start = self.index 110a5f9918aSopenharmony_ci while self.data[self.index] not in ', \n\0': 111a5f9918aSopenharmony_ci self.index += 1 112a5f9918aSopenharmony_ci value = self.data[start:self.index] 113a5f9918aSopenharmony_ci return TokenClass(value, None, None) 114a5f9918aSopenharmony_ci 115a5f9918aSopenharmony_ci def scan_tag(self): 116a5f9918aSopenharmony_ci self.index += 1 117a5f9918aSopenharmony_ci start = self.index 118a5f9918aSopenharmony_ci while self.data[self.index] not in ' \n\0': 119a5f9918aSopenharmony_ci self.index += 1 120a5f9918aSopenharmony_ci value = self.data[start:self.index] 121a5f9918aSopenharmony_ci if not value: 122a5f9918aSopenharmony_ci value = '!' 123a5f9918aSopenharmony_ci elif value[0] == '!': 124a5f9918aSopenharmony_ci value = 'tag:yaml.org,2002:'+value[1:] 125a5f9918aSopenharmony_ci elif value[0] == '<' and value[-1] == '>': 126a5f9918aSopenharmony_ci value = value[1:-1] 127a5f9918aSopenharmony_ci else: 128a5f9918aSopenharmony_ci value = '!'+value 129a5f9918aSopenharmony_ci return yaml.TagToken(value, None, None) 130a5f9918aSopenharmony_ci 131a5f9918aSopenharmony_ci QUOTE_CODES = { 132a5f9918aSopenharmony_ci 'x': 2, 133a5f9918aSopenharmony_ci 'u': 4, 134a5f9918aSopenharmony_ci 'U': 8, 135a5f9918aSopenharmony_ci } 136a5f9918aSopenharmony_ci 137a5f9918aSopenharmony_ci QUOTE_REPLACES = { 138a5f9918aSopenharmony_ci '\\': '\\', 139a5f9918aSopenharmony_ci '\"': '\"', 140a5f9918aSopenharmony_ci ' ': ' ', 141a5f9918aSopenharmony_ci 'a': '\x07', 142a5f9918aSopenharmony_ci 'b': '\x08', 143a5f9918aSopenharmony_ci 'e': '\x1B', 144a5f9918aSopenharmony_ci 'f': '\x0C', 145a5f9918aSopenharmony_ci 'n': '\x0A', 146a5f9918aSopenharmony_ci 'r': '\x0D', 147a5f9918aSopenharmony_ci 't': '\x09', 148a5f9918aSopenharmony_ci 'v': '\x0B', 149a5f9918aSopenharmony_ci 'N': '\u0085', 150a5f9918aSopenharmony_ci 'L': '\u2028', 151a5f9918aSopenharmony_ci 'P': '\u2029', 152a5f9918aSopenharmony_ci '_': '_', 153a5f9918aSopenharmony_ci '0': '\x00', 154a5f9918aSopenharmony_ci } 155a5f9918aSopenharmony_ci 156a5f9918aSopenharmony_ci def scan_scalar(self): 157a5f9918aSopenharmony_ci self.index += 1 158a5f9918aSopenharmony_ci chunks = [] 159a5f9918aSopenharmony_ci start = self.index 160a5f9918aSopenharmony_ci ignore_spaces = False 161a5f9918aSopenharmony_ci while self.data[self.index] != '"': 162a5f9918aSopenharmony_ci if self.data[self.index] == '\\': 163a5f9918aSopenharmony_ci ignore_spaces = False 164a5f9918aSopenharmony_ci chunks.append(self.data[start:self.index]) 165a5f9918aSopenharmony_ci self.index += 1 166a5f9918aSopenharmony_ci ch = self.data[self.index] 167a5f9918aSopenharmony_ci self.index += 1 168a5f9918aSopenharmony_ci if ch == '\n': 169a5f9918aSopenharmony_ci ignore_spaces = True 170a5f9918aSopenharmony_ci elif ch in self.QUOTE_CODES: 171a5f9918aSopenharmony_ci length = self.QUOTE_CODES[ch] 172a5f9918aSopenharmony_ci code = int(self.data[self.index:self.index+length], 16) 173a5f9918aSopenharmony_ci chunks.append(chr(code)) 174a5f9918aSopenharmony_ci self.index += length 175a5f9918aSopenharmony_ci else: 176a5f9918aSopenharmony_ci if ch not in self.QUOTE_REPLACES: 177a5f9918aSopenharmony_ci raise CanonicalError("invalid escape code") 178a5f9918aSopenharmony_ci chunks.append(self.QUOTE_REPLACES[ch]) 179a5f9918aSopenharmony_ci start = self.index 180a5f9918aSopenharmony_ci elif self.data[self.index] == '\n': 181a5f9918aSopenharmony_ci chunks.append(self.data[start:self.index]) 182a5f9918aSopenharmony_ci chunks.append(' ') 183a5f9918aSopenharmony_ci self.index += 1 184a5f9918aSopenharmony_ci start = self.index 185a5f9918aSopenharmony_ci ignore_spaces = True 186a5f9918aSopenharmony_ci elif ignore_spaces and self.data[self.index] == ' ': 187a5f9918aSopenharmony_ci self.index += 1 188a5f9918aSopenharmony_ci start = self.index 189a5f9918aSopenharmony_ci else: 190a5f9918aSopenharmony_ci ignore_spaces = False 191a5f9918aSopenharmony_ci self.index += 1 192a5f9918aSopenharmony_ci chunks.append(self.data[start:self.index]) 193a5f9918aSopenharmony_ci self.index += 1 194a5f9918aSopenharmony_ci return yaml.ScalarToken(''.join(chunks), False, None, None) 195a5f9918aSopenharmony_ci 196a5f9918aSopenharmony_ci def find_token(self): 197a5f9918aSopenharmony_ci found = False 198a5f9918aSopenharmony_ci while not found: 199a5f9918aSopenharmony_ci while self.data[self.index] in ' \t': 200a5f9918aSopenharmony_ci self.index += 1 201a5f9918aSopenharmony_ci if self.data[self.index] == '#': 202a5f9918aSopenharmony_ci while self.data[self.index] != '\n': 203a5f9918aSopenharmony_ci self.index += 1 204a5f9918aSopenharmony_ci if self.data[self.index] == '\n': 205a5f9918aSopenharmony_ci self.index += 1 206a5f9918aSopenharmony_ci else: 207a5f9918aSopenharmony_ci found = True 208a5f9918aSopenharmony_ci 209a5f9918aSopenharmony_ciclass CanonicalParser: 210a5f9918aSopenharmony_ci 211a5f9918aSopenharmony_ci def __init__(self): 212a5f9918aSopenharmony_ci self.events = [] 213a5f9918aSopenharmony_ci self.parsed = False 214a5f9918aSopenharmony_ci 215a5f9918aSopenharmony_ci def dispose(self): 216a5f9918aSopenharmony_ci pass 217a5f9918aSopenharmony_ci 218a5f9918aSopenharmony_ci # stream: STREAM-START document* STREAM-END 219a5f9918aSopenharmony_ci def parse_stream(self): 220a5f9918aSopenharmony_ci self.get_token(yaml.StreamStartToken) 221a5f9918aSopenharmony_ci self.events.append(yaml.StreamStartEvent(None, None)) 222a5f9918aSopenharmony_ci while not self.check_token(yaml.StreamEndToken): 223a5f9918aSopenharmony_ci if self.check_token(yaml.DirectiveToken, yaml.DocumentStartToken): 224a5f9918aSopenharmony_ci self.parse_document() 225a5f9918aSopenharmony_ci else: 226a5f9918aSopenharmony_ci raise CanonicalError("document is expected, got "+repr(self.tokens[0])) 227a5f9918aSopenharmony_ci self.get_token(yaml.StreamEndToken) 228a5f9918aSopenharmony_ci self.events.append(yaml.StreamEndEvent(None, None)) 229a5f9918aSopenharmony_ci 230a5f9918aSopenharmony_ci # document: DIRECTIVE? DOCUMENT-START node 231a5f9918aSopenharmony_ci def parse_document(self): 232a5f9918aSopenharmony_ci node = None 233a5f9918aSopenharmony_ci if self.check_token(yaml.DirectiveToken): 234a5f9918aSopenharmony_ci self.get_token(yaml.DirectiveToken) 235a5f9918aSopenharmony_ci self.get_token(yaml.DocumentStartToken) 236a5f9918aSopenharmony_ci self.events.append(yaml.DocumentStartEvent(None, None)) 237a5f9918aSopenharmony_ci self.parse_node() 238a5f9918aSopenharmony_ci self.events.append(yaml.DocumentEndEvent(None, None)) 239a5f9918aSopenharmony_ci 240a5f9918aSopenharmony_ci # node: ALIAS | ANCHOR? TAG? (SCALAR|sequence|mapping) 241a5f9918aSopenharmony_ci def parse_node(self): 242a5f9918aSopenharmony_ci if self.check_token(yaml.AliasToken): 243a5f9918aSopenharmony_ci self.events.append(yaml.AliasEvent(self.get_token_value(), None, None)) 244a5f9918aSopenharmony_ci else: 245a5f9918aSopenharmony_ci anchor = None 246a5f9918aSopenharmony_ci if self.check_token(yaml.AnchorToken): 247a5f9918aSopenharmony_ci anchor = self.get_token_value() 248a5f9918aSopenharmony_ci tag = None 249a5f9918aSopenharmony_ci if self.check_token(yaml.TagToken): 250a5f9918aSopenharmony_ci tag = self.get_token_value() 251a5f9918aSopenharmony_ci if self.check_token(yaml.ScalarToken): 252a5f9918aSopenharmony_ci self.events.append(yaml.ScalarEvent(anchor, tag, (False, False), self.get_token_value(), None, None)) 253a5f9918aSopenharmony_ci elif self.check_token(yaml.FlowSequenceStartToken): 254a5f9918aSopenharmony_ci self.events.append(yaml.SequenceStartEvent(anchor, tag, None, None)) 255a5f9918aSopenharmony_ci self.parse_sequence() 256a5f9918aSopenharmony_ci elif self.check_token(yaml.FlowMappingStartToken): 257a5f9918aSopenharmony_ci self.events.append(yaml.MappingStartEvent(anchor, tag, None, None)) 258a5f9918aSopenharmony_ci self.parse_mapping() 259a5f9918aSopenharmony_ci else: 260a5f9918aSopenharmony_ci raise CanonicalError("SCALAR, '[', or '{' is expected, got "+repr(self.tokens[0])) 261a5f9918aSopenharmony_ci 262a5f9918aSopenharmony_ci # sequence: SEQUENCE-START (node (ENTRY node)*)? ENTRY? SEQUENCE-END 263a5f9918aSopenharmony_ci def parse_sequence(self): 264a5f9918aSopenharmony_ci self.get_token(yaml.FlowSequenceStartToken) 265a5f9918aSopenharmony_ci if not self.check_token(yaml.FlowSequenceEndToken): 266a5f9918aSopenharmony_ci self.parse_node() 267a5f9918aSopenharmony_ci while not self.check_token(yaml.FlowSequenceEndToken): 268a5f9918aSopenharmony_ci self.get_token(yaml.FlowEntryToken) 269a5f9918aSopenharmony_ci if not self.check_token(yaml.FlowSequenceEndToken): 270a5f9918aSopenharmony_ci self.parse_node() 271a5f9918aSopenharmony_ci self.get_token(yaml.FlowSequenceEndToken) 272a5f9918aSopenharmony_ci self.events.append(yaml.SequenceEndEvent(None, None)) 273a5f9918aSopenharmony_ci 274a5f9918aSopenharmony_ci # mapping: MAPPING-START (map_entry (ENTRY map_entry)*)? ENTRY? MAPPING-END 275a5f9918aSopenharmony_ci def parse_mapping(self): 276a5f9918aSopenharmony_ci self.get_token(yaml.FlowMappingStartToken) 277a5f9918aSopenharmony_ci if not self.check_token(yaml.FlowMappingEndToken): 278a5f9918aSopenharmony_ci self.parse_map_entry() 279a5f9918aSopenharmony_ci while not self.check_token(yaml.FlowMappingEndToken): 280a5f9918aSopenharmony_ci self.get_token(yaml.FlowEntryToken) 281a5f9918aSopenharmony_ci if not self.check_token(yaml.FlowMappingEndToken): 282a5f9918aSopenharmony_ci self.parse_map_entry() 283a5f9918aSopenharmony_ci self.get_token(yaml.FlowMappingEndToken) 284a5f9918aSopenharmony_ci self.events.append(yaml.MappingEndEvent(None, None)) 285a5f9918aSopenharmony_ci 286a5f9918aSopenharmony_ci # map_entry: KEY node VALUE node 287a5f9918aSopenharmony_ci def parse_map_entry(self): 288a5f9918aSopenharmony_ci self.get_token(yaml.KeyToken) 289a5f9918aSopenharmony_ci self.parse_node() 290a5f9918aSopenharmony_ci self.get_token(yaml.ValueToken) 291a5f9918aSopenharmony_ci self.parse_node() 292a5f9918aSopenharmony_ci 293a5f9918aSopenharmony_ci def parse(self): 294a5f9918aSopenharmony_ci self.parse_stream() 295a5f9918aSopenharmony_ci self.parsed = True 296a5f9918aSopenharmony_ci 297a5f9918aSopenharmony_ci def get_event(self): 298a5f9918aSopenharmony_ci if not self.parsed: 299a5f9918aSopenharmony_ci self.parse() 300a5f9918aSopenharmony_ci return self.events.pop(0) 301a5f9918aSopenharmony_ci 302a5f9918aSopenharmony_ci def check_event(self, *choices): 303a5f9918aSopenharmony_ci if not self.parsed: 304a5f9918aSopenharmony_ci self.parse() 305a5f9918aSopenharmony_ci if self.events: 306a5f9918aSopenharmony_ci if not choices: 307a5f9918aSopenharmony_ci return True 308a5f9918aSopenharmony_ci for choice in choices: 309a5f9918aSopenharmony_ci if isinstance(self.events[0], choice): 310a5f9918aSopenharmony_ci return True 311a5f9918aSopenharmony_ci return False 312a5f9918aSopenharmony_ci 313a5f9918aSopenharmony_ci def peek_event(self): 314a5f9918aSopenharmony_ci if not self.parsed: 315a5f9918aSopenharmony_ci self.parse() 316a5f9918aSopenharmony_ci return self.events[0] 317a5f9918aSopenharmony_ci 318a5f9918aSopenharmony_ciclass CanonicalLoader(CanonicalScanner, CanonicalParser, 319a5f9918aSopenharmony_ci yaml.composer.Composer, yaml.constructor.Constructor, yaml.resolver.Resolver): 320a5f9918aSopenharmony_ci 321a5f9918aSopenharmony_ci def __init__(self, stream): 322a5f9918aSopenharmony_ci if hasattr(stream, 'read'): 323a5f9918aSopenharmony_ci stream = stream.read() 324a5f9918aSopenharmony_ci CanonicalScanner.__init__(self, stream) 325a5f9918aSopenharmony_ci CanonicalParser.__init__(self) 326a5f9918aSopenharmony_ci yaml.composer.Composer.__init__(self) 327a5f9918aSopenharmony_ci yaml.constructor.Constructor.__init__(self) 328a5f9918aSopenharmony_ci yaml.resolver.Resolver.__init__(self) 329a5f9918aSopenharmony_ci 330a5f9918aSopenharmony_ciyaml.CanonicalLoader = CanonicalLoader 331a5f9918aSopenharmony_ci 332a5f9918aSopenharmony_cidef canonical_scan(stream): 333a5f9918aSopenharmony_ci return yaml.scan(stream, Loader=CanonicalLoader) 334a5f9918aSopenharmony_ci 335a5f9918aSopenharmony_ciyaml.canonical_scan = canonical_scan 336a5f9918aSopenharmony_ci 337a5f9918aSopenharmony_cidef canonical_parse(stream): 338a5f9918aSopenharmony_ci return yaml.parse(stream, Loader=CanonicalLoader) 339a5f9918aSopenharmony_ci 340a5f9918aSopenharmony_ciyaml.canonical_parse = canonical_parse 341a5f9918aSopenharmony_ci 342a5f9918aSopenharmony_cidef canonical_compose(stream): 343a5f9918aSopenharmony_ci return yaml.compose(stream, Loader=CanonicalLoader) 344a5f9918aSopenharmony_ci 345a5f9918aSopenharmony_ciyaml.canonical_compose = canonical_compose 346a5f9918aSopenharmony_ci 347a5f9918aSopenharmony_cidef canonical_compose_all(stream): 348a5f9918aSopenharmony_ci return yaml.compose_all(stream, Loader=CanonicalLoader) 349a5f9918aSopenharmony_ci 350a5f9918aSopenharmony_ciyaml.canonical_compose_all = canonical_compose_all 351a5f9918aSopenharmony_ci 352a5f9918aSopenharmony_cidef canonical_load(stream): 353a5f9918aSopenharmony_ci return yaml.load(stream, Loader=CanonicalLoader) 354a5f9918aSopenharmony_ci 355a5f9918aSopenharmony_ciyaml.canonical_load = canonical_load 356a5f9918aSopenharmony_ci 357a5f9918aSopenharmony_cidef canonical_load_all(stream): 358a5f9918aSopenharmony_ci return yaml.load_all(stream, Loader=CanonicalLoader) 359a5f9918aSopenharmony_ci 360a5f9918aSopenharmony_ciyaml.canonical_load_all = canonical_load_all 361a5f9918aSopenharmony_ci 362