diff options
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/parser/consumption/presentation.py')
-rw-r--r-- | azure/aria/aria-extension-cloudify/src/aria/aria/parser/consumption/presentation.py | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/parser/consumption/presentation.py b/azure/aria/aria-extension-cloudify/src/aria/aria/parser/consumption/presentation.py new file mode 100644 index 0000000..542b3f0 --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/parser/consumption/presentation.py @@ -0,0 +1,137 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + + +from ...utils.threading import FixedThreadPoolExecutor +from ...utils.formatting import json_dumps, yaml_dumps +from ..loading import UriLocation +from ..reading import AlreadyReadException +from ..presentation import PresenterNotFoundError +from .consumer import Consumer + + +class Read(Consumer): + """ + Reads the presentation, handling imports recursively. + + It works by consuming a data source via appropriate :class:`~aria.parser.loading.Loader`, + :class:`~aria.parser.reading.Reader`, and :class:`~aria.parser.presentation.Presenter` + instances. + + It supports agnostic raw data composition for presenters that have + ``_get_import_locations`` and ``_merge_import``. + + To improve performance, loaders are called asynchronously on separate threads. + + Note that parsing may internally trigger more than one loading/reading/presentation + cycle, for example if the agnostic raw data has dependencies that must also be parsed. + """ + + def consume(self): + if self.context.presentation.location is None: + self.context.validation.report('Presentation consumer: missing location') + return + + presenter = None + imported_presentations = None + + executor = FixedThreadPoolExecutor(size=self.context.presentation.threads, + timeout=self.context.presentation.timeout) + executor.print_exceptions = self.context.presentation.print_exceptions + try: + presenter = self._present(self.context.presentation.location, None, None, executor) + executor.drain() + + # Handle exceptions + for e in executor.exceptions: + self._handle_exception(e) + + imported_presentations = executor.returns + finally: + executor.close() + + # Merge imports + if (imported_presentations is not None) and hasattr(presenter, '_merge_import'): + for imported_presentation in imported_presentations: + okay = True + if hasattr(presenter, '_validate_import'): + okay = presenter._validate_import(self.context, imported_presentation) + if okay: + presenter._merge_import(imported_presentation) + + self.context.presentation.presenter = presenter + + def dump(self): + if self.context.has_arg_switch('yaml'): + indent = self.context.get_arg_value_int('indent', 2) + raw = self.context.presentation.presenter._raw + self.context.write(yaml_dumps(raw, indent=indent)) + elif self.context.has_arg_switch('json'): + indent = self.context.get_arg_value_int('indent', 2) + raw = self.context.presentation.presenter._raw + self.context.write(json_dumps(raw, indent=indent)) + else: + self.context.presentation.presenter._dump(self.context) + + def _handle_exception(self, e): + if isinstance(e, AlreadyReadException): + return + super(Read, self)._handle_exception(e) + + def _present(self, location, origin_location, presenter_class, executor): + # Link the context to this thread + self.context.set_thread_local() + + raw = self._read(location, origin_location) + + if self.context.presentation.presenter_class is not None: + # The presenter class we specified in the context overrides everything + presenter_class = self.context.presentation.presenter_class + else: + try: + presenter_class = self.context.presentation.presenter_source.get_presenter(raw) + except PresenterNotFoundError: + if presenter_class is None: + raise + # We'll use the presenter class we were given (from the presenter that imported us) + if presenter_class is None: + raise PresenterNotFoundError('presenter not found') + + presentation = presenter_class(raw=raw) + + if presentation is not None and hasattr(presentation, '_link_locators'): + presentation._link_locators() + + # Submit imports to executor + if hasattr(presentation, '_get_import_locations'): + import_locations = presentation._get_import_locations(self.context) + if import_locations: + for import_location in import_locations: + # The imports inherit the parent presenter class and use the current location as + # their origin location + import_location = UriLocation(import_location) + executor.submit(self._present, import_location, location, presenter_class, + executor) + + return presentation + + def _read(self, location, origin_location): + if self.context.reading.reader is not None: + return self.context.reading.reader.read() + loader = self.context.loading.loader_source.get_loader(self.context.loading, location, + origin_location) + reader = self.context.reading.reader_source.get_reader(self.context.reading, location, + loader) + return reader.read() |