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.
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."""
40 'application: %s' % application
,
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.
55 xml_str: The XML string.
57 A list of DispatchEntry objects defining how URLs are dispatched to
60 AppEngineConfigException: In case of malformed XML or illegal inputs.
64 self
.dispatch_entries
= []
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
)
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.
91 node: <dispatch> XML node in dos.xml.
93 tag
= xml_parser_utils
.GetTag(node
)
95 self
.errors
.append('Unrecognized node: <%s>' % tag
)
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
)
104 self
.errors
.append(validation
)
106 self
.dispatch_entries
.append(entry
)
108 def _ValidateEntry(self
, entry
):
112 return MISSING_MODULE
115 class DispatchEntry(object):
116 """Instances contain information about individual dispatch entries."""
120 "- url: '%s'" % self
._SanitizeForYaml
(self
.url
),
121 ' module: %s' % self
.module
,
124 def _SanitizeForYaml(self
, dirty_str
):
125 return dirty_str
.replace("'", r
"\'")