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