1.9.30 sync.
[gae.git] / python / google / appengine / api / croninfo.py
blob949a2a9e8aaf3b3721a5ee37583c037877c52ad5
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.
21 """CronInfo tools.
23 A library for working with CronInfo records, describing cron entries for an
24 application. Supports loading the records from yaml.
25 """
34 import logging
35 import sys
36 import traceback
38 try:
39 import pytz
40 except ImportError:
41 pytz = None
43 from google.appengine.cron import groc
44 from google.appengine.cron import groctimespecification
45 from google.appengine.api import appinfo
46 from google.appengine.api import validation
47 from google.appengine.api import yaml_builder
48 from google.appengine.api import yaml_listener
49 from google.appengine.api import yaml_object
51 _URL_REGEX = r'^/.*$'
52 _TIMEZONE_REGEX = r'^.{0,100}$'
53 _DESCRIPTION_REGEX = ur'^.{0,499}$'
56 SERVER_ID_RE_STRING = r'(?!-)[a-z\d\-]{1,63}'
59 SERVER_VERSION_RE_STRING = r'(?!-)[a-z\d\-]{1,100}'
60 _VERSION_REGEX = r'^(?:(?:(%s):)?)(%s)$' % (SERVER_ID_RE_STRING,
61 SERVER_VERSION_RE_STRING)
66 class GrocValidator(validation.Validator):
67 """Checks that a schedule is in valid groc format."""
69 def Validate(self, value, key=None):
70 """Validates a schedule."""
71 if value is None:
72 raise validation.MissingAttribute('schedule must be specified')
73 if not isinstance(value, basestring):
74 raise TypeError('schedule must be a string, not \'%r\''%type(value))
75 try:
76 groctimespecification.GrocTimeSpecification(value)
77 except groc.GrocException, e:
78 raise validation.ValidationError('schedule \'%s\' failed to parse: %s'%(
79 value, e.args[0]))
80 return value
83 class TimezoneValidator(validation.Validator):
84 """Checks that a timezone can be correctly parsed and is known."""
86 def Validate(self, value, key=None):
87 """Validates a timezone."""
88 if value is None:
90 return
91 if not isinstance(value, basestring):
92 raise TypeError('timezone must be a string, not \'%r\'' % type(value))
93 if pytz is None:
95 return value
96 try:
97 pytz.timezone(value)
98 except pytz.UnknownTimeZoneError:
99 raise validation.ValidationError('timezone \'%s\' is unknown' % value)
100 except IOError:
103 return value
104 except:
107 unused_e, v, t = sys.exc_info()
108 logging.warning('pytz raised an unexpected error: %s.\n' % (v) +
109 'Traceback:\n' + '\n'.join(traceback.format_tb(t)))
110 raise
111 return value
114 CRON = 'cron'
116 URL = 'url'
117 SCHEDULE = 'schedule'
118 TIMEZONE = 'timezone'
119 DESCRIPTION = 'description'
120 TARGET = 'target'
123 class MalformedCronfigurationFile(Exception):
124 """Configuration file for Cron is malformed."""
125 pass
128 class CronEntry(validation.Validated):
129 """A cron entry describes a single cron job."""
130 ATTRIBUTES = {
131 URL: _URL_REGEX,
132 SCHEDULE: GrocValidator(),
133 TIMEZONE: TimezoneValidator(),
134 DESCRIPTION: validation.Optional(_DESCRIPTION_REGEX),
135 TARGET: validation.Optional(_VERSION_REGEX),
139 class CronInfoExternal(validation.Validated):
140 """CronInfoExternal describes all cron entries for an application."""
141 ATTRIBUTES = {
142 appinfo.APPLICATION: validation.Optional(appinfo.APPLICATION_RE_STRING),
143 CRON: validation.Optional(validation.Repeated(CronEntry))
147 def LoadSingleCron(cron_info, open_fn=None):
148 """Load a cron.yaml file or string and return a CronInfoExternal object."""
149 builder = yaml_object.ObjectBuilder(CronInfoExternal)
150 handler = yaml_builder.BuilderHandler(builder)
151 listener = yaml_listener.EventListener(handler)
152 listener.Parse(cron_info)
154 cron_info = handler.GetResults()
155 if len(cron_info) < 1:
156 raise MalformedCronfigurationFile('Empty cron configuration.')
157 if len(cron_info) > 1:
158 raise MalformedCronfigurationFile('Multiple cron sections '
159 'in configuration.')
160 return cron_info[0]