thirdparty/google_appengine/google/appengine/api/yaml_builder.py
changeset 109 620f9b141567
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/yaml_builder.py	Tue Aug 26 21:49:54 2008 +0000
@@ -0,0 +1,432 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""PyYAML event builder handler
+
+Receives events from YAML listener and forwards them to a builder
+object so that it can construct a properly structured object.
+"""
+
+
+
+
+
+from google.appengine.api import yaml_errors
+from google.appengine.api import yaml_listener
+
+import yaml
+
+_TOKEN_DOCUMENT = 'document'
+_TOKEN_SEQUENCE = 'sequence'
+_TOKEN_MAPPING = 'mapping'
+_TOKEN_KEY = 'key'
+_TOKEN_VALUES = frozenset((
+  _TOKEN_DOCUMENT,
+  _TOKEN_SEQUENCE,
+  _TOKEN_MAPPING,
+  _TOKEN_KEY))
+
+
+class Builder(object):
+  """Interface for building documents and type from YAML events.
+
+  Implement this interface to create a new builder.  Builders are
+  passed to the BuilderHandler and used as a factory and assembler
+  for creating concrete representations of YAML files.
+  """
+
+  def BuildDocument(self):
+    """Build new document.
+
+    The object built by this method becomes the top level entity
+    that the builder handler constructs.  The actual type is
+    determined by the sub-class of the Builder class and can essentially
+    be any type at all.  This method is always called when the parser
+    encounters the start of a new document.
+
+    Returns:
+      New object instance representing concrete document which is
+      returned to user via BuilderHandler.GetResults().
+    """
+
+  def InitializeDocument(self, document, value):
+    """Initialize document with value from top level of document.
+
+    This method is called when the root document element is encountered at
+    the top level of a YAML document.  It should get called immediately
+    after BuildDocument.
+
+    Receiving the None value indicates the empty document.
+
+    Args:
+      document: Document as constructed in BuildDocument.
+      value: Scalar value to initialize the document with.
+    """
+
+  def BuildMapping(self, top_value):
+    """Build a new mapping representation.
+
+    Called when StartMapping event received.  Type of object is determined
+    by Builder sub-class.
+
+    Args:
+      top_value: Object which will be new mappings parant.  Will be object
+        returned from previous call to BuildMapping or BuildSequence.
+
+    Returns:
+      Instance of new object that represents a mapping type in target model.
+    """
+
+  def EndMapping(self, top_value, mapping):
+    """Previously constructed mapping scope is at an end.
+
+    Called when the end of a mapping block is encountered.  Useful for
+    additional clean up or end of scope validation.
+
+    Args:
+      top_value: Value which is parent of the mapping.
+      mapping: Mapping which is at the end of its scope.
+    """
+
+  def BuildSequence(self, top_value):
+    """Build a new sequence representation.
+
+    Called when StartSequence event received.  Type of object is determined
+    by Builder sub-class.
+
+    Args:
+      top_value: Object which will be new sequences parant.  Will be object
+        returned from previous call to BuildMapping or BuildSequence.
+
+    Returns:
+      Instance of new object that represents a sequence type in target model.
+    """
+
+  def EndSequence(self, top_value, sequence):
+    """Previously constructed sequence scope is at an end.
+
+    Called when the end of a sequence block is encountered.  Useful for
+    additional clean up or end of scope validation.
+
+    Args:
+      top_value: Value which is parent of the sequence.
+      sequence: Sequence which is at the end of its scope.
+    """
+
+  def MapTo(self, subject, key, value):
+    """Map value to a mapping representation.
+
+    Implementation is defined by sub-class of Builder.
+
+    Args:
+      subject: Object that represents mapping.  Value returned from
+        BuildMapping.
+      key: Key used to map value to subject.  Can be any scalar value.
+      value: Value which is mapped to subject. Can be any kind of value.
+    """
+
+  def AppendTo(self, subject, value):
+    """Append value to a sequence representation.
+
+    Implementation is defined by sub-class of Builder.
+
+    Args:
+      subject: Object that represents sequence.  Value returned from
+        BuildSequence
+      value: Value to be appended to subject.  Can be any kind of value.
+    """
+
+
+class BuilderHandler(yaml_listener.EventHandler):
+  """PyYAML event handler used to build objects.
+
+  Maintains state information as it receives parse events so that object
+  nesting is maintained.  Uses provided builder object to construct and
+  assemble objects as it goes.
+
+  As it receives events from the YAML parser, it builds a stack of data
+  representing structural tokens.  As the scope of documents, mappings
+  and sequences end, those token, value pairs are popped from the top of
+  the stack so that the original scope can resume processing.
+
+  A special case is made for the _KEY token.  It represents a temporary
+  value which only occurs inside mappings.  It is immediately popped off
+  the stack when it's associated value is encountered in the parse stream.
+  It is necessary to do this because the YAML parser does not combine
+  key and value information in to a single event.
+  """
+
+  def __init__(self, builder):
+    """Initialization for builder handler.
+
+    Args:
+      builder: Instance of Builder class.
+
+    Raises:
+      ListenerConfigurationError when builder is not a Builder class.
+    """
+    if not isinstance(builder, Builder):
+      raise yaml_errors.ListenerConfigurationError(
+        'Must provide builder of type yaml_listener.Builder')
+    self._builder = builder
+    self._stack = None
+    self._top = None
+    self._results = []
+
+  def _Push(self, token, value):
+    """Push values to stack at start of nesting.
+
+    When a new object scope is beginning, will push the token (type of scope)
+    along with the new objects value, the latter of which is provided through
+    the various build methods of the builder.
+
+    Args:
+      token: Token indicating the type of scope which is being created; must
+        belong to _TOKEN_VALUES.
+      value: Value to associate with given token.  Construction of value is
+        determined by the builder provided to this handler at construction.
+    """
+    self._top = (token, value)
+    self._stack.append(self._top)
+
+  def _Pop(self):
+    """Pop values from stack at end of nesting.
+
+    Called to indicate the end of a nested scope.
+
+    Returns:
+      Previously pushed value at the top of the stack.
+    """
+    assert self._stack != [] and self._stack is not None
+    token, value = self._stack.pop()
+    if self._stack:
+      self._top = self._stack[-1]
+    else:
+      self._top = None
+    return value
+
+  def _HandleAnchor(self, event):
+    """Handle anchor attached to event.
+
+    Currently will raise an error if anchor is used.  Anchors are used to
+    define a document wide tag to a given value (scalar, mapping or sequence).
+
+    Args:
+      event: Event which may have anchor property set.
+
+    Raises:
+      NotImplementedError if event attempts to use an anchor.
+    """
+    if hasattr(event, 'anchor') and event.anchor is not None:
+      raise NotImplementedError, 'Anchors not supported in this handler'
+
+  def _HandleValue(self, value):
+    """Handle given value based on state of parser
+
+    This method handles the various values that are created by the builder
+    at the beginning of scope events (such as mappings and sequences) or
+    when a scalar value is received.
+
+    Method is called when handler receives a parser, MappingStart or
+    SequenceStart.
+
+    Args:
+      value: Value received as scalar value or newly constructed mapping or
+        sequence instance.
+
+    Raises:
+      InternalError if the building process encounters an unexpected token.
+      This is an indication of an implementation error in BuilderHandler.
+    """
+    token, top_value = self._top
+
+    if token == _TOKEN_KEY:
+      key = self._Pop()
+      mapping_token, mapping = self._top
+      assert _TOKEN_MAPPING == mapping_token
+      self._builder.MapTo(mapping, key, value)
+
+    elif token == _TOKEN_MAPPING:
+      self._Push(_TOKEN_KEY, value)
+
+    elif token == _TOKEN_SEQUENCE:
+      self._builder.AppendTo(top_value, value)
+
+    elif token == _TOKEN_DOCUMENT:
+      self._builder.InitializeDocument(top_value, value)
+
+    else:
+      raise yaml_errors.InternalError('Unrecognized builder token:\n%s' % token)
+
+  def StreamStart(self, event, loader):
+    """Initializes internal state of handler
+
+    Args:
+      event: Ignored.
+    """
+    assert self._stack is None
+    self._stack = []
+    self._top = None
+    self._results = []
+
+  def StreamEnd(self, event, loader):
+    """Cleans up internal state of handler after parsing
+
+    Args:
+      event: Ignored.
+    """
+    assert self._stack == [] and self._top is None
+    self._stack = None
+
+  def DocumentStart(self, event, loader):
+    """Build new document.
+
+    Pushes new document on to stack.
+
+    Args:
+      event: Ignored.
+    """
+    assert self._stack == []
+    self._Push(_TOKEN_DOCUMENT, self._builder.BuildDocument())
+
+  def DocumentEnd(self, event, loader):
+    """End of document.
+
+    Args:
+      event: Ignored.
+    """
+    assert self._top[0] == _TOKEN_DOCUMENT
+    self._results.append(self._Pop())
+
+  def Alias(self, event, loader):
+    """Not implemented yet.
+
+    Args:
+      event: Ignored.
+    """
+    raise NotImplementedError('Anchors not supported in this handler')
+
+  def Scalar(self, event, loader):
+    """Handle scalar value
+
+    Since scalars are simple values that are passed directly in by the
+    parser, handle like any value with no additional processing.
+
+    Of course, key values will be handles specially.  A key value is recognized
+    when the top token is _TOKEN_MAPPING.
+
+    Args:
+      event: Event containing scalar value.
+    """
+    self._HandleAnchor(event)
+    if event.tag is None and self._top[0] != _TOKEN_MAPPING:
+      try:
+        tag = loader.resolve(yaml.nodes.ScalarNode,
+                             event.value, event.implicit)
+      except IndexError:
+        tag = loader.DEFAULT_SCALAR_TAG
+    else:
+      tag = event.tag
+
+    if tag is None:
+      value = event.value
+    else:
+      node = yaml.nodes.ScalarNode(tag,
+                                   event.value,
+                                   event.start_mark,
+                                   event.end_mark,
+                                   event.style)
+      value = loader.construct_object(node)
+    self._HandleValue(value)
+
+  def SequenceStart(self, event, loader):
+    """Start of sequence scope
+
+    Create a new sequence from the builder and then handle in the context
+    of its parent.
+
+    Args:
+      event: SequenceStartEvent generated by loader.
+      loader: Loader that generated event.
+    """
+    self._HandleAnchor(event)
+    token, parent = self._top
+
+    if token == _TOKEN_KEY:
+      token, parent = self._stack[-2]
+    sequence = self._builder.BuildSequence(parent)
+    self._HandleValue(sequence)
+    self._Push(_TOKEN_SEQUENCE, sequence)
+
+  def SequenceEnd(self, event, loader):
+    """End of sequence.
+
+    Args:
+      event: Ignored
+      loader: Ignored.
+      """
+    assert self._top[0] == _TOKEN_SEQUENCE
+    end_object = self._Pop()
+    top_value = self._top[1]
+    self._builder.EndSequence(top_value, end_object)
+
+  def MappingStart(self, event, loader):
+    """Start of mapping scope.
+
+    Create a mapping from builder and then handle in the context of its
+    parent.
+
+    Args:
+      event: MappingStartEvent generated by loader.
+      loader: Loader that generated event.
+    """
+    self._HandleAnchor(event)
+    token, parent = self._top
+
+    if token == _TOKEN_KEY:
+      token, parent = self._stack[-2]
+    mapping = self._builder.BuildMapping(parent)
+    self._HandleValue(mapping)
+    self._Push(_TOKEN_MAPPING, mapping)
+
+  def MappingEnd(self, event, loader):
+    """End of mapping
+
+    Args:
+      event: Ignored.
+      loader: Ignored.
+    """
+    assert self._top[0] == _TOKEN_MAPPING
+    end_object = self._Pop()
+    top_value = self._top[1]
+    self._builder.EndMapping(top_value, end_object)
+
+  def GetResults(self):
+    """Get results of document stream processing.
+
+    This method can be invoked after fully parsing the entire YAML file
+    to retrieve constructed contents of YAML file.  Called after EndStream.
+
+    Returns:
+      A tuple of all document objects that were parsed from YAML stream.
+
+    Raises:
+      InternalError if the builder stack is not empty by the end of parsing.
+    """
+    if self._stack is not None:
+      raise yaml_errors.InternalError('Builder stack is not empty.')
+    return tuple(self._results)