195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python 295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved. 301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be 401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file. 501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Class for reading GRD files into memory, without processing them. 701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org''' 801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgimport os.path 1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types 1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax 1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax.handler 1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception 15b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.orgfrom grit import util 1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import base 1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import mapping 1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import misc 1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass StopParsingException(Exception): 2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''An exception used to stop parsing.''' 2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pass 2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass GrdContentHandler(xml.sax.handler.ContentHandler): 27976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org def __init__(self, stop_after, debug, dir, defines, tags_to_ignore, 28976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org target_platform): 2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Invariant of data: 3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # 'root' is the root of the parse tree being created, or None if we haven't 3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # parsed out any elements. 3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # 'stack' is the a stack of elements that we push new nodes onto and 3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # pop from when they finish parsing, or [] if we are not currently parsing. 3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # 'stack[-1]' is the top of the stack. 3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.root = None 3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.stack = [] 3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.stop_after = stop_after 3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.debug = debug 3983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.dir = dir 4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.defines = defines 4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.tags_to_ignore = tags_to_ignore or set() 4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ignore_depth = 0 43976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org self.target_platform = target_platform 4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def startElement(self, name, attrs): 4683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.ignore_depth or name in self.tags_to_ignore: 4783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.debug and self.ignore_depth == 0: 4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print "Ignoring element %s and its children" % name 4983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.ignore_depth += 1 5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return 5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.debug: 5383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org attr_list = ' '.join('%s="%s"' % kv for kv in attrs.items()) 5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print ("Starting parsing of element %s with attributes %r" % 5583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org (name, attr_list or '(none)')) 5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 5783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org typeattr = attrs.get('type') 5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node = mapping.ElementToClass(name, typeattr)() 5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 6083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.stack: 6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.stack[-1].AddChild(node) 6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.StartParsing(name, self.stack[-1]) 6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org else: 6483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org assert self.root is None 6583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.root = node 66976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org if isinstance(self.root, misc.GritNode): 67976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org if self.target_platform: 68976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org self.root.SetTargetPlatform(self.target_platform) 6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org node.StartParsing(name, None) 7083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.defines: 7183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org node.SetDefines(self.defines) 7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.stack.append(node) 7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org for attr, attrval in attrs.items(): 7583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org node.HandleAttribute(attr, attrval) 7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def endElement(self, name): 7883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.ignore_depth: 7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.ignore_depth -= 1 8083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org return 8183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 8283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if name == 'part': 8383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org partnode = self.stack[-1] 8483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org partnode.started_inclusion = True 8583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org # Add the contents of the sub-grd file as children of the <part> node. 8683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org partname = partnode.GetInputPath() 8783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if os.path.dirname(partname): 8883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org # TODO(benrg): Remove this limitation. (The problem is that GRIT 8983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org # assumes that files referenced from the GRD file are relative to 9083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org # a path stored in the root <grit> node.) 9183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org raise exception.GotPathExpectedFilenameOnly() 9283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org partname = os.path.join(self.dir, partname) 9383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org # Exceptions propagate to the handler in grd_reader.Parse(). 9483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org xml.sax.parse(partname, GrdPartContentHandler(self)) 9583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 9683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.debug: 9783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org print "End parsing of element %s" % name 9883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.stack.pop().EndParsing() 9983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 10083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if name == self.stop_after: 10183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org raise StopParsingException() 10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def characters(self, content): 10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.ignore_depth == 0: 10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if self.stack[-1]: 10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org self.stack[-1].AppendContent(content) 10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org def ignorableWhitespace(self, whitespace): 10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # TODO(joi) This is not supported by expat. Should use a different XML parser? 11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pass 11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 11383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgclass GrdPartContentHandler(xml.sax.handler.ContentHandler): 11483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org def __init__(self, parent): 11583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.parent = parent 11683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.depth = 0 11783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 11883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org def startElement(self, name, attrs): 11983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.depth: 12083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.parent.startElement(name, attrs) 12183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org else: 12283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if name != 'grit-part': 12383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org raise exception.MissingElement("root tag must be <grit-part>") 12483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if attrs: 12583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org raise exception.UnexpectedAttribute( 12683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org "<grit-part> tag must not have attributes") 12783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.depth += 1 12883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 12983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org def endElement(self, name): 13083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.depth -= 1 13183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if self.depth: 13283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.parent.endElement(name) 13383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 13483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org def characters(self, content): 13583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.parent.characters(content) 13683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 13783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org def ignorableWhitespace(self, whitespace): 13883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org self.parent.ignorableWhitespace(whitespace) 13983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 14083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 141ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.orgdef Parse(filename_or_stream, dir=None, stop_after=None, first_ids_file=None, 142abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org debug=False, defines=None, tags_to_ignore=None, target_platform=None): 14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org '''Parses a GRD file into a tree of nodes (from grit.node). 14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org If filename_or_stream is a stream, 'dir' should point to the directory 14683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org notionally containing the stream (this feature is only used in unit tests). 14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org If 'stop_after' is provided, the parsing will stop once the first node 14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org with this name has been fully parsed (including all its contents). 15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org If 'debug' is true, lots of information about the parsing events will be 15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org printed out during parsing of the file. 15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 1543a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org If 'first_ids_file' is non-empty, it is used to override the setting for the 1553a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org first_ids_file attribute of the <grit> root node. Note that the first_ids_file 1563a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org parameter should be relative to the cwd, even though the first_ids_file 1573a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org attribute of the <grit> node is relative to the grd file. 15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 159abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org If 'target_platform' is set, this is used to determine the target 160abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org platform of builds, instead of using |sys.platform|. 161abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org 16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Args: 16383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org filename_or_stream: './bla.xml' 16483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org dir: None (if filename_or_stream is a filename) or '.' 16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org stop_after: 'inputs' 1660dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org first_ids_file: 'GRIT_DIR/../gritsettings/resource_ids' 16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org debug: False 16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org defines: dictionary of defines, like {'chromeos': '1'} 169abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org target_platform: None or the value that would be returned by sys.platform 170abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org on your target platform. 17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Return: 17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Subclass of grit.node.base.Node 17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org Throws: 17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org grit.exception.Parsing 17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org ''' 17883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 17983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if dir is None and isinstance(filename_or_stream, types.StringType): 18083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org dir = util.dirname(filename_or_stream) 18183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 18283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir, 183976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org defines=defines, tags_to_ignore=tags_to_ignore, 184976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org target_platform=target_platform) 18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org try: 18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org xml.sax.parse(filename_or_stream, handler) 18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org except StopParsingException: 18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org assert stop_after 18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org pass 19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org except: 19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org if not debug: 19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print "parse exception: run GRIT with the -x flag to debug .grd problems" 19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org raise 19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 195ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org if handler.root.name != 'grit': 196ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org raise exception.MissingElement("root tag must be <grit>") 19783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org 19883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org if hasattr(handler.root, 'SetOwnDir'): 19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org # Fix up the base_dir so it is relative to the input file. 20083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org assert dir is not None 20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org handler.root.SetOwnDir(dir) 20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 2030dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org if isinstance(handler.root, misc.GritNode): 2040dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org if first_ids_file: 2053a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org # Make the path to the first_ids_file relative to the grd file, 2063a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org # unless it begins with GRIT_DIR. 2073a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org GRIT_DIR_PREFIX = 'GRIT_DIR' 2083a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org if not (first_ids_file.startswith(GRIT_DIR_PREFIX) 2093a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org and first_ids_file[len(GRIT_DIR_PREFIX)] in ['/', '\\']): 2103a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org rel_dir = os.path.relpath(os.getcwd(), dir) 2113a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org first_ids_file = util.normpath(os.path.join(rel_dir, first_ids_file)) 2120dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org handler.root.attrs['first_ids_file'] = first_ids_file 2130dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org # Assign first ids to the nodes that don't have them. 2140dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org handler.root.AssignFirstIds(filename_or_stream, defines) 21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org return handler.root 21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org 21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgif __name__ == '__main__': 22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org util.ChangeStdoutEncoding() 22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org print unicode(Parse(sys.argv[1])) 222