thirdparty/google_appengine/google/appengine/api/yaml_builder.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 builder handler
       
    19 
       
    20 Receives events from YAML listener and forwards them to a builder
       
    21 object so that it can construct a properly structured object.
       
    22 """
       
    23 
       
    24 
       
    25 
       
    26 
       
    27 
       
    28 from google.appengine.api import yaml_errors
       
    29 from google.appengine.api import yaml_listener
       
    30 
       
    31 import yaml
       
    32 
       
    33 _TOKEN_DOCUMENT = 'document'
       
    34 _TOKEN_SEQUENCE = 'sequence'
       
    35 _TOKEN_MAPPING = 'mapping'
       
    36 _TOKEN_KEY = 'key'
       
    37 _TOKEN_VALUES = frozenset((
       
    38   _TOKEN_DOCUMENT,
       
    39   _TOKEN_SEQUENCE,
       
    40   _TOKEN_MAPPING,
       
    41   _TOKEN_KEY))
       
    42 
       
    43 
       
    44 class Builder(object):
       
    45   """Interface for building documents and type from YAML events.
       
    46 
       
    47   Implement this interface to create a new builder.  Builders are
       
    48   passed to the BuilderHandler and used as a factory and assembler
       
    49   for creating concrete representations of YAML files.
       
    50   """
       
    51 
       
    52   def BuildDocument(self):
       
    53     """Build new document.
       
    54 
       
    55     The object built by this method becomes the top level entity
       
    56     that the builder handler constructs.  The actual type is
       
    57     determined by the sub-class of the Builder class and can essentially
       
    58     be any type at all.  This method is always called when the parser
       
    59     encounters the start of a new document.
       
    60 
       
    61     Returns:
       
    62       New object instance representing concrete document which is
       
    63       returned to user via BuilderHandler.GetResults().
       
    64     """
       
    65 
       
    66   def InitializeDocument(self, document, value):
       
    67     """Initialize document with value from top level of document.
       
    68 
       
    69     This method is called when the root document element is encountered at
       
    70     the top level of a YAML document.  It should get called immediately
       
    71     after BuildDocument.
       
    72 
       
    73     Receiving the None value indicates the empty document.
       
    74 
       
    75     Args:
       
    76       document: Document as constructed in BuildDocument.
       
    77       value: Scalar value to initialize the document with.
       
    78     """
       
    79 
       
    80   def BuildMapping(self, top_value):
       
    81     """Build a new mapping representation.
       
    82 
       
    83     Called when StartMapping event received.  Type of object is determined
       
    84     by Builder sub-class.
       
    85 
       
    86     Args:
       
    87       top_value: Object which will be new mappings parant.  Will be object
       
    88         returned from previous call to BuildMapping or BuildSequence.
       
    89 
       
    90     Returns:
       
    91       Instance of new object that represents a mapping type in target model.
       
    92     """
       
    93 
       
    94   def EndMapping(self, top_value, mapping):
       
    95     """Previously constructed mapping scope is at an end.
       
    96 
       
    97     Called when the end of a mapping block is encountered.  Useful for
       
    98     additional clean up or end of scope validation.
       
    99 
       
   100     Args:
       
   101       top_value: Value which is parent of the mapping.
       
   102       mapping: Mapping which is at the end of its scope.
       
   103     """
       
   104 
       
   105   def BuildSequence(self, top_value):
       
   106     """Build a new sequence representation.
       
   107 
       
   108     Called when StartSequence event received.  Type of object is determined
       
   109     by Builder sub-class.
       
   110 
       
   111     Args:
       
   112       top_value: Object which will be new sequences parant.  Will be object
       
   113         returned from previous call to BuildMapping or BuildSequence.
       
   114 
       
   115     Returns:
       
   116       Instance of new object that represents a sequence type in target model.
       
   117     """
       
   118 
       
   119   def EndSequence(self, top_value, sequence):
       
   120     """Previously constructed sequence scope is at an end.
       
   121 
       
   122     Called when the end of a sequence block is encountered.  Useful for
       
   123     additional clean up or end of scope validation.
       
   124 
       
   125     Args:
       
   126       top_value: Value which is parent of the sequence.
       
   127       sequence: Sequence which is at the end of its scope.
       
   128     """
       
   129 
       
   130   def MapTo(self, subject, key, value):
       
   131     """Map value to a mapping representation.
       
   132 
       
   133     Implementation is defined by sub-class of Builder.
       
   134 
       
   135     Args:
       
   136       subject: Object that represents mapping.  Value returned from
       
   137         BuildMapping.
       
   138       key: Key used to map value to subject.  Can be any scalar value.
       
   139       value: Value which is mapped to subject. Can be any kind of value.
       
   140     """
       
   141 
       
   142   def AppendTo(self, subject, value):
       
   143     """Append value to a sequence representation.
       
   144 
       
   145     Implementation is defined by sub-class of Builder.
       
   146 
       
   147     Args:
       
   148       subject: Object that represents sequence.  Value returned from
       
   149         BuildSequence
       
   150       value: Value to be appended to subject.  Can be any kind of value.
       
   151     """
       
   152 
       
   153 
       
   154 class BuilderHandler(yaml_listener.EventHandler):
       
   155   """PyYAML event handler used to build objects.
       
   156 
       
   157   Maintains state information as it receives parse events so that object
       
   158   nesting is maintained.  Uses provided builder object to construct and
       
   159   assemble objects as it goes.
       
   160 
       
   161   As it receives events from the YAML parser, it builds a stack of data
       
   162   representing structural tokens.  As the scope of documents, mappings
       
   163   and sequences end, those token, value pairs are popped from the top of
       
   164   the stack so that the original scope can resume processing.
       
   165 
       
   166   A special case is made for the _KEY token.  It represents a temporary
       
   167   value which only occurs inside mappings.  It is immediately popped off
       
   168   the stack when it's associated value is encountered in the parse stream.
       
   169   It is necessary to do this because the YAML parser does not combine
       
   170   key and value information in to a single event.
       
   171   """
       
   172 
       
   173   def __init__(self, builder):
       
   174     """Initialization for builder handler.
       
   175 
       
   176     Args:
       
   177       builder: Instance of Builder class.
       
   178 
       
   179     Raises:
       
   180       ListenerConfigurationError when builder is not a Builder class.
       
   181     """
       
   182     if not isinstance(builder, Builder):
       
   183       raise yaml_errors.ListenerConfigurationError(
       
   184         'Must provide builder of type yaml_listener.Builder')
       
   185     self._builder = builder
       
   186     self._stack = None
       
   187     self._top = None
       
   188     self._results = []
       
   189 
       
   190   def _Push(self, token, value):
       
   191     """Push values to stack at start of nesting.
       
   192 
       
   193     When a new object scope is beginning, will push the token (type of scope)
       
   194     along with the new objects value, the latter of which is provided through
       
   195     the various build methods of the builder.
       
   196 
       
   197     Args:
       
   198       token: Token indicating the type of scope which is being created; must
       
   199         belong to _TOKEN_VALUES.
       
   200       value: Value to associate with given token.  Construction of value is
       
   201         determined by the builder provided to this handler at construction.
       
   202     """
       
   203     self._top = (token, value)
       
   204     self._stack.append(self._top)
       
   205 
       
   206   def _Pop(self):
       
   207     """Pop values from stack at end of nesting.
       
   208 
       
   209     Called to indicate the end of a nested scope.
       
   210 
       
   211     Returns:
       
   212       Previously pushed value at the top of the stack.
       
   213     """
       
   214     assert self._stack != [] and self._stack is not None
       
   215     token, value = self._stack.pop()
       
   216     if self._stack:
       
   217       self._top = self._stack[-1]
       
   218     else:
       
   219       self._top = None
       
   220     return value
       
   221 
       
   222   def _HandleAnchor(self, event):
       
   223     """Handle anchor attached to event.
       
   224 
       
   225     Currently will raise an error if anchor is used.  Anchors are used to
       
   226     define a document wide tag to a given value (scalar, mapping or sequence).
       
   227 
       
   228     Args:
       
   229       event: Event which may have anchor property set.
       
   230 
       
   231     Raises:
       
   232       NotImplementedError if event attempts to use an anchor.
       
   233     """
       
   234     if hasattr(event, 'anchor') and event.anchor is not None:
       
   235       raise NotImplementedError, 'Anchors not supported in this handler'
       
   236 
       
   237   def _HandleValue(self, value):
       
   238     """Handle given value based on state of parser
       
   239 
       
   240     This method handles the various values that are created by the builder
       
   241     at the beginning of scope events (such as mappings and sequences) or
       
   242     when a scalar value is received.
       
   243 
       
   244     Method is called when handler receives a parser, MappingStart or
       
   245     SequenceStart.
       
   246 
       
   247     Args:
       
   248       value: Value received as scalar value or newly constructed mapping or
       
   249         sequence instance.
       
   250 
       
   251     Raises:
       
   252       InternalError if the building process encounters an unexpected token.
       
   253       This is an indication of an implementation error in BuilderHandler.
       
   254     """
       
   255     token, top_value = self._top
       
   256 
       
   257     if token == _TOKEN_KEY:
       
   258       key = self._Pop()
       
   259       mapping_token, mapping = self._top
       
   260       assert _TOKEN_MAPPING == mapping_token
       
   261       self._builder.MapTo(mapping, key, value)
       
   262 
       
   263     elif token == _TOKEN_MAPPING:
       
   264       self._Push(_TOKEN_KEY, value)
       
   265 
       
   266     elif token == _TOKEN_SEQUENCE:
       
   267       self._builder.AppendTo(top_value, value)
       
   268 
       
   269     elif token == _TOKEN_DOCUMENT:
       
   270       self._builder.InitializeDocument(top_value, value)
       
   271 
       
   272     else:
       
   273       raise yaml_errors.InternalError('Unrecognized builder token:\n%s' % token)
       
   274 
       
   275   def StreamStart(self, event, loader):
       
   276     """Initializes internal state of handler
       
   277 
       
   278     Args:
       
   279       event: Ignored.
       
   280     """
       
   281     assert self._stack is None
       
   282     self._stack = []
       
   283     self._top = None
       
   284     self._results = []
       
   285 
       
   286   def StreamEnd(self, event, loader):
       
   287     """Cleans up internal state of handler after parsing
       
   288 
       
   289     Args:
       
   290       event: Ignored.
       
   291     """
       
   292     assert self._stack == [] and self._top is None
       
   293     self._stack = None
       
   294 
       
   295   def DocumentStart(self, event, loader):
       
   296     """Build new document.
       
   297 
       
   298     Pushes new document on to stack.
       
   299 
       
   300     Args:
       
   301       event: Ignored.
       
   302     """
       
   303     assert self._stack == []
       
   304     self._Push(_TOKEN_DOCUMENT, self._builder.BuildDocument())
       
   305 
       
   306   def DocumentEnd(self, event, loader):
       
   307     """End of document.
       
   308 
       
   309     Args:
       
   310       event: Ignored.
       
   311     """
       
   312     assert self._top[0] == _TOKEN_DOCUMENT
       
   313     self._results.append(self._Pop())
       
   314 
       
   315   def Alias(self, event, loader):
       
   316     """Not implemented yet.
       
   317 
       
   318     Args:
       
   319       event: Ignored.
       
   320     """
       
   321     raise NotImplementedError('Anchors not supported in this handler')
       
   322 
       
   323   def Scalar(self, event, loader):
       
   324     """Handle scalar value
       
   325 
       
   326     Since scalars are simple values that are passed directly in by the
       
   327     parser, handle like any value with no additional processing.
       
   328 
       
   329     Of course, key values will be handles specially.  A key value is recognized
       
   330     when the top token is _TOKEN_MAPPING.
       
   331 
       
   332     Args:
       
   333       event: Event containing scalar value.
       
   334     """
       
   335     self._HandleAnchor(event)
       
   336     if event.tag is None and self._top[0] != _TOKEN_MAPPING:
       
   337       try:
       
   338         tag = loader.resolve(yaml.nodes.ScalarNode,
       
   339                              event.value, event.implicit)
       
   340       except IndexError:
       
   341         tag = loader.DEFAULT_SCALAR_TAG
       
   342     else:
       
   343       tag = event.tag
       
   344 
       
   345     if tag is None:
       
   346       value = event.value
       
   347     else:
       
   348       node = yaml.nodes.ScalarNode(tag,
       
   349                                    event.value,
       
   350                                    event.start_mark,
       
   351                                    event.end_mark,
       
   352                                    event.style)
       
   353       value = loader.construct_object(node)
       
   354     self._HandleValue(value)
       
   355 
       
   356   def SequenceStart(self, event, loader):
       
   357     """Start of sequence scope
       
   358 
       
   359     Create a new sequence from the builder and then handle in the context
       
   360     of its parent.
       
   361 
       
   362     Args:
       
   363       event: SequenceStartEvent generated by loader.
       
   364       loader: Loader that generated event.
       
   365     """
       
   366     self._HandleAnchor(event)
       
   367     token, parent = self._top
       
   368 
       
   369     if token == _TOKEN_KEY:
       
   370       token, parent = self._stack[-2]
       
   371     sequence = self._builder.BuildSequence(parent)
       
   372     self._HandleValue(sequence)
       
   373     self._Push(_TOKEN_SEQUENCE, sequence)
       
   374 
       
   375   def SequenceEnd(self, event, loader):
       
   376     """End of sequence.
       
   377 
       
   378     Args:
       
   379       event: Ignored
       
   380       loader: Ignored.
       
   381       """
       
   382     assert self._top[0] == _TOKEN_SEQUENCE
       
   383     end_object = self._Pop()
       
   384     top_value = self._top[1]
       
   385     self._builder.EndSequence(top_value, end_object)
       
   386 
       
   387   def MappingStart(self, event, loader):
       
   388     """Start of mapping scope.
       
   389 
       
   390     Create a mapping from builder and then handle in the context of its
       
   391     parent.
       
   392 
       
   393     Args:
       
   394       event: MappingStartEvent generated by loader.
       
   395       loader: Loader that generated event.
       
   396     """
       
   397     self._HandleAnchor(event)
       
   398     token, parent = self._top
       
   399 
       
   400     if token == _TOKEN_KEY:
       
   401       token, parent = self._stack[-2]
       
   402     mapping = self._builder.BuildMapping(parent)
       
   403     self._HandleValue(mapping)
       
   404     self._Push(_TOKEN_MAPPING, mapping)
       
   405 
       
   406   def MappingEnd(self, event, loader):
       
   407     """End of mapping
       
   408 
       
   409     Args:
       
   410       event: Ignored.
       
   411       loader: Ignored.
       
   412     """
       
   413     assert self._top[0] == _TOKEN_MAPPING
       
   414     end_object = self._Pop()
       
   415     top_value = self._top[1]
       
   416     self._builder.EndMapping(top_value, end_object)
       
   417 
       
   418   def GetResults(self):
       
   419     """Get results of document stream processing.
       
   420 
       
   421     This method can be invoked after fully parsing the entire YAML file
       
   422     to retrieve constructed contents of YAML file.  Called after EndStream.
       
   423 
       
   424     Returns:
       
   425       A tuple of all document objects that were parsed from YAML stream.
       
   426 
       
   427     Raises:
       
   428       InternalError if the builder stack is not empty by the end of parsing.
       
   429     """
       
   430     if self._stack is not None:
       
   431       raise yaml_errors.InternalError('Builder stack is not empty.')
       
   432     return tuple(self._results)