diff -r 261778de26ff -r 620f9b141567 thirdparty/google_appengine/google/appengine/api/yaml_builder.py --- /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)