1.9.30 sync.
[gae.git] / python / google / appengine / tools / cron_xml_parser.py
blobca63131a835d1da99ec954b6d49b83db2b6e6e7a
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 cron.xml.
19 CronXmlParser is called with an XML string to produce a CronXml object
20 containing the data from the XML.
22 CronXmlParser: converts XML to CronXml objct
23 Cron: describes a single cron specified in cron.xml
24 """
26 from xml.etree import ElementTree
28 from google.appengine.cron import groc
29 from google.appengine.cron import groctimespecification
30 from google.appengine.tools import xml_parser_utils
31 from google.appengine.tools.app_engine_config_exception import AppEngineConfigException
34 def GetCronYaml(unused_application, cron_xml_str):
35 return _MakeCronListIntoYaml(CronXmlParser().ProcessXml(cron_xml_str))
38 def _MakeCronListIntoYaml(cron_list):
39 """Converts list of yaml statements describing cron jobs into a string."""
40 statements = ['cron:']
41 for cron in cron_list:
42 statements += cron.ToYaml()
43 return '\n'.join(statements) + '\n'
46 class CronXmlParser(object):
47 """Provides logic for walking down XML tree and pulling data."""
49 def ProcessXml(self, xml_str):
50 """Parses XML string and returns object representation of relevant info.
52 Args:
53 xml_str: The XML string.
54 Returns:
55 A list of Cron objects containing information about cron jobs from the
56 XML.
57 Raises:
58 AppEngineConfigException: In case of malformed XML or illegal inputs.
59 """
61 try:
62 self.crons = []
63 self.errors = []
64 xml_root = ElementTree.fromstring(xml_str)
65 if xml_root.tag != 'cronentries':
66 raise AppEngineConfigException('Root tag must be <cronentries>')
68 for child in xml_root.getchildren():
69 self.ProcessCronNode(child)
71 if self.errors:
72 raise AppEngineConfigException('\n'.join(self.errors))
74 return self.crons
75 except ElementTree.ParseError:
76 raise AppEngineConfigException('Bad input -- not valid XML')
78 def ProcessCronNode(self, node):
79 """Processes XML <cron> nodes into Cron objects.
81 The following information is parsed out:
82 description: Describing the purpose of the cron job.
83 url: The location of the script.
84 schedule: Written in groc; the schedule according to which the job is
85 executed.
86 timezone: The timezone that the schedule runs in.
87 target: Which version of the app this applies to.
89 Args:
90 node: <cron> XML node in cron.xml.
91 """
92 tag = xml_parser_utils.GetTag(node)
93 if tag != 'cron':
94 self.errors.append('Unrecognized node: <%s>' % tag)
95 return
97 cron = Cron()
98 cron.url = xml_parser_utils.GetChildNodeText(node, 'url')
99 cron.timezone = xml_parser_utils.GetChildNodeText(node, 'timezone')
100 cron.target = xml_parser_utils.GetChildNodeText(node, 'target')
101 cron.description = xml_parser_utils.GetChildNodeText(node, 'description')
102 cron.schedule = xml_parser_utils.GetChildNodeText(node, 'schedule')
104 validation_error = self._ValidateCronEntry(cron)
105 if validation_error:
106 self.errors.append(validation_error)
107 else:
108 self.crons.append(cron)
110 def _ValidateCronEntry(self, cron):
113 if not cron.url:
114 return 'No URL for <cron> entry'
115 if not cron.schedule:
116 return "No schedule provided for <cron> entry with URL '%s'" % cron.url
117 try:
118 groctimespecification.GrocTimeSpecification(cron.schedule)
119 except groc.GrocException:
120 return ("Text '%s' in <schedule> node failed to parse,"
121 ' for <cron> entry with url %s.'
122 % (cron.schedule, cron.url))
125 class Cron(object):
126 """Instances contain information about individual cron entries."""
127 TZ_GMT = 'UTC'
129 def ToYaml(self):
130 """Returns data from Cron object as a list of Yaml statements."""
131 statements = [
132 '- url: %s' % self._SanitizeForYaml(self.url),
133 ' schedule: %s' % self._SanitizeForYaml(self.schedule)]
134 for optional in ('target', 'timezone', 'description'):
135 field = getattr(self, optional)
136 if field:
137 statements.append(' %s: %s' % (optional, self._SanitizeForYaml(field)))
138 return statements
140 def _SanitizeForYaml(self, field):
141 return "'%s'" % field.replace('\n', ' ').replace("'", "''")