1.9.30 sync.
[gae.git] / python / google / appengine / api / yaml_listener.py
blobabccc732f1ed38e4425b6b6ee92007153d445e69
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 """PyYAML event listener
23 Contains class which interprets YAML events and forwards them to
24 a handler object.
25 """
29 from google.appengine.api import yaml_errors
30 import yaml
34 _EVENT_METHOD_MAP = {
35 yaml.events.StreamStartEvent: 'StreamStart',
36 yaml.events.StreamEndEvent: 'StreamEnd',
37 yaml.events.DocumentStartEvent: 'DocumentStart',
38 yaml.events.DocumentEndEvent: 'DocumentEnd',
39 yaml.events.AliasEvent: 'Alias',
40 yaml.events.ScalarEvent: 'Scalar',
41 yaml.events.SequenceStartEvent: 'SequenceStart',
42 yaml.events.SequenceEndEvent: 'SequenceEnd',
43 yaml.events.MappingStartEvent: 'MappingStart',
44 yaml.events.MappingEndEvent: 'MappingEnd',
48 class EventHandler(object):
49 """Handler interface for parsing YAML files.
51 Implement this interface to define specific YAML event handling class.
52 Implementing classes instances are passed to the constructor of
53 EventListener to act as a receiver of YAML parse events.
54 """
55 def StreamStart(self, event, loader):
56 """Handle start of stream event"""
58 def StreamEnd(self, event, loader):
59 """Handle end of stream event"""
61 def DocumentStart(self, event, loader):
62 """Handle start of document event"""
64 def DocumentEnd(self, event, loader):
65 """Handle end of document event"""
67 def Alias(self, event, loader):
68 """Handle alias event"""
70 def Scalar(self, event, loader):
71 """Handle scalar event"""
73 def SequenceStart(self, event, loader):
74 """Handle start of sequence event"""
76 def SequenceEnd(self, event, loader):
77 """Handle end of sequence event"""
79 def MappingStart(self, event, loader):
80 """Handle start of mappping event"""
82 def MappingEnd(self, event, loader):
83 """Handle end of mapping event"""
86 class EventListener(object):
87 """Helper class to re-map PyYAML events to method calls.
89 By default, PyYAML generates its events via a Python generator. This class
90 is a helper that iterates over the events from the PyYAML parser and forwards
91 them to a handle class in the form of method calls. For simplicity, the
92 underlying event is forwarded to the handler as a parameter to the call.
94 This object does not itself produce iterable objects, but is really a mapping
95 to a given handler instance.
97 Example use:
99 class PrintDocumentHandler(object):
100 def DocumentStart(event):
101 print "A new document has been started"
103 EventListener(PrintDocumentHandler()).Parse('''
104 key1: value1
106 key2: value2
109 >>> A new document has been started
110 A new document has been started
112 In the example above, the implemented handler class (PrintDocumentHandler)
113 has a single method which reports each time a new document is started within
114 a YAML file. It is not necessary to subclass the EventListener, merely it
115 receives a PrintDocumentHandler instance. Every time a new document begins,
116 PrintDocumentHandler.DocumentStart is called with the PyYAML event passed
117 in as its parameter..
120 def __init__(self, event_handler):
121 """Initialize PyYAML event listener.
123 Constructs internal mapping directly from event type to method on actual
124 handler. This prevents reflection being used during actual parse time.
126 Args:
127 event_handler: Event handler that will receive mapped events. Must
128 implement at least one appropriate handler method named from
129 the values of the _EVENT_METHOD_MAP.
131 Raises:
132 ListenerConfigurationError if event_handler is not an EventHandler.
134 if not isinstance(event_handler, EventHandler):
135 raise yaml_errors.ListenerConfigurationError(
136 'Must provide event handler of type yaml_listener.EventHandler')
137 self._event_method_map = {}
139 for event, method in _EVENT_METHOD_MAP.iteritems():
141 self._event_method_map[event] = getattr(event_handler, method)
143 def HandleEvent(self, event, loader=None):
144 """Handle individual PyYAML event.
146 Args:
147 event: Event to forward to method call in method call.
149 Raises:
150 IllegalEvent when receives an unrecognized or unsupported event type.
153 if event.__class__ not in _EVENT_METHOD_MAP:
154 raise yaml_errors.IllegalEvent(
155 "%s is not a valid PyYAML class" % event.__class__.__name__)
157 if event.__class__ in self._event_method_map:
158 self._event_method_map[event.__class__](event, loader)
160 def _HandleEvents(self, events):
161 """Iterate over all events and send them to handler.
163 This method is not meant to be called from the interface.
165 Only use in tests.
167 Args:
168 events: Iterator or generator containing events to process.
169 raises:
170 EventListenerParserError when a yaml.parser.ParserError is raised.
171 EventError when an exception occurs during the handling of an event.
173 for event in events:
174 try:
175 self.HandleEvent(*event)
176 except Exception, e:
177 event_object, loader = event
178 raise yaml_errors.EventError(e, event_object)
180 def _GenerateEventParameters(self,
181 stream,
182 loader_class=yaml.loader.SafeLoader):
183 """Creates a generator that yields event, loader parameter pairs.
185 For use as parameters to HandleEvent method for use by Parse method.
186 During testing, _GenerateEventParameters is simulated by allowing
187 the harness to pass in a list of pairs as the parameter.
189 A list of (event, loader) pairs must be passed to _HandleEvents otherwise
190 it is not possible to pass the loader instance to the handler.
192 Also responsible for instantiating the loader from the Loader
193 parameter.
195 Args:
196 stream: String document or open file object to process as per the
197 yaml.parse method. Any object that implements a 'read()' method which
198 returns a string document will work.
199 Loader: Loader class to use as per the yaml.parse method. Used to
200 instantiate new yaml.loader instance.
202 Yields:
203 Tuple(event, loader) where:
204 event: Event emitted by PyYAML loader.
205 loader_class: Used for dependency injection.
207 assert loader_class is not None
208 try:
209 loader = loader_class(stream)
210 while loader.check_event():
211 yield (loader.get_event(), loader)
212 except yaml.error.YAMLError, e:
213 raise yaml_errors.EventListenerYAMLError(e)
215 def Parse(self, stream, loader_class=yaml.loader.SafeLoader):
216 """Call YAML parser to generate and handle all events.
218 Calls PyYAML parser and sends resulting generator to handle_event method
219 for processing.
221 Args:
222 stream: String document or open file object to process as per the
223 yaml.parse method. Any object that implements a 'read()' method which
224 returns a string document will work with the YAML parser.
225 loader_class: Used for dependency injection.
227 self._HandleEvents(self._GenerateEventParameters(stream, loader_class))