1.9.30 sync.
[gae.git] / python / google / appengine / tools / dispatch_xml_parser.py
blob0832b95146d4088949dbb8e2db85e792c92dad86
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Directly processes text of dispatch.xml.
19 DispatchXmlParser is called with an XML string to produce a list of
20 DispatchEntry objects containing the data from the XML.
21 """
23 from xml.etree import ElementTree
25 from google.appengine.tools import xml_parser_utils
26 from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
28 MISSING_URL = '<dispatch> node must contain a <url>'
29 MISSING_MODULE = '<dispatch> node must contain a <module>'
32 def GetDispatchYaml(application, dispatch_xml_str):
33 return _MakeDispatchListIntoYaml(
34 application, DispatchXmlParser().ProcessXml(dispatch_xml_str))
37 def _MakeDispatchListIntoYaml(application, dispatch_list):
38 """Converts list of DispatchEntry objects into a YAML string."""
39 statements = [
40 'application: %s' % application,
41 'dispatch:',
43 for entry in dispatch_list:
44 statements += entry.ToYaml()
45 return '\n'.join(statements) + '\n'
48 class DispatchXmlParser(object):
49 """Provides logic for walking down XML tree and pulling data."""
51 def ProcessXml(self, xml_str):
52 """Parses XML string and returns object representation of relevant info.
54 Args:
55 xml_str: The XML string.
56 Returns:
57 A list of DispatchEntry objects defining how URLs are dispatched to
58 modules.
59 Raises:
60 AppEngineConfigException: In case of malformed XML or illegal inputs.
61 """
63 try:
64 self.dispatch_entries = []
65 self.errors = []
66 xml_root = ElementTree.fromstring(xml_str)
67 if xml_root.tag != 'dispatch-entries':
68 raise AppEngineConfigException('Root tag must be <dispatch-entries>')
70 for child in xml_root.getchildren():
71 self.ProcessDispatchNode(child)
73 if self.errors:
74 raise AppEngineConfigException('\n'.join(self.errors))
76 return self.dispatch_entries
77 except ElementTree.ParseError:
78 raise AppEngineConfigException('Bad input -- not valid XML')
80 def ProcessDispatchNode(self, node):
81 """Processes XML <dispatch> nodes into DispatchEntry objects.
83 The following information is parsed out:
84 url: The URL or URL pattern to route.
85 module: The module to route it to.
86 If there are no errors, the data is loaded into a DispatchEntry object
87 and added to a list. Upon error, a description of the error is added to
88 a list and the method terminates.
90 Args:
91 node: <dispatch> XML node in dos.xml.
92 """
93 tag = xml_parser_utils.GetTag(node)
94 if tag != 'dispatch':
95 self.errors.append('Unrecognized node: <%s>' % tag)
96 return
98 entry = DispatchEntry()
99 entry.url = xml_parser_utils.GetChildNodeText(node, 'url')
100 entry.module = xml_parser_utils.GetChildNodeText(node, 'module')
102 validation = self._ValidateEntry(entry)
103 if validation:
104 self.errors.append(validation)
105 return
106 self.dispatch_entries.append(entry)
108 def _ValidateEntry(self, entry):
109 if not entry.url:
110 return MISSING_URL
111 if not entry.module:
112 return MISSING_MODULE
115 class DispatchEntry(object):
116 """Instances contain information about individual dispatch entries."""
118 def ToYaml(self):
119 return [
120 "- url: '%s'" % self._SanitizeForYaml(self.url),
121 ' module: %s' % self.module,
124 def _SanitizeForYaml(self, dirty_str):
125 return dirty_str.replace("'", r"\'")