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
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.
53 xml_str: The XML string.
55 A list of Cron objects containing information about cron jobs from the
58 AppEngineConfigException: In case of malformed XML or illegal inputs.
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
)
72 raise AppEngineConfigException('\n'.join(self
.errors
))
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
86 timezone: The timezone that the schedule runs in.
87 target: Which version of the app this applies to.
90 node: <cron> XML node in cron.xml.
92 tag
= xml_parser_utils
.GetTag(node
)
94 self
.errors
.append('Unrecognized node: <%s>' % tag
)
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
)
106 self
.errors
.append(validation_error
)
108 self
.crons
.append(cron
)
110 def _ValidateCronEntry(self
, cron
):
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
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
))
126 """Instances contain information about individual cron entries."""
130 """Returns data from Cron object as a list of Yaml 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
)
137 statements
.append(' %s: %s' % (optional
, self
._SanitizeForYaml
(field
)))
140 def _SanitizeForYaml(self
, field
):
141 return "'%s'" % field
.replace('\n', ' ').replace("'", "''")