--- /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)