Github user mxmrlv commented on a diff in the pull request: https://github.com/apache/incubator-ariatosca/pull/207#discussion_r150775602 --- Diff: aria/parser/consumption/presentation.py --- @@ -86,52 +73,193 @@ def dump(self): self.context.presentation.presenter._dump(self.context) def _handle_exception(self, e): - if isinstance(e, AlreadyReadException): + if isinstance(e, _Skip): return super(Read, self)._handle_exception(e) - def _present(self, location, origin_location, presenter_class, executor): + def _present_all(self): + location = self.context.presentation.location + + if location is None: + self.context.validation.report('Read consumer: missing location') + return + + executor = self.context.presentation.create_executor() + try: + # This call may recursively submit tasks to the executor if there are imports + main = self._present(location, None, None, executor) + + # Wait for all tasks to complete + executor.drain() + + # Handle exceptions + for e in executor.exceptions: + self._handle_exception(e) + + results = executor.returns or [] + finally: + executor.close() + + results.insert(0, main) + + return main, results + + def _present(self, location, origin_canonical_location, origin_presenter_class, executor): # Link the context to this thread self.context.set_thread_local() - raw = self._read(location, origin_location) + # Canonicalize the location + if self.context.reading.reader is None: + loader, canonical_location = self._create_loader(location, origin_canonical_location) + else: + # If a reader is specified in the context then we skip loading + loader = None + canonical_location = location + + # Skip self imports + if canonical_location == origin_canonical_location: + raise _Skip() + + if self.context.presentation.cache: + # Is the presentation in the global cache? + try: + presentation = PRESENTATION_CACHE[canonical_location] + return _Result(presentation, canonical_location, origin_canonical_location) + except KeyError: + pass + + try: + # Is the presentation in the local cache? + presentation = self._cache[canonical_location] + return _Result(presentation, canonical_location, origin_canonical_location) + except KeyError: + pass + + # Create and cache new presentation + presentation = self._create_presentation(canonical_location, loader, origin_presenter_class) + self._cache[canonical_location] = presentation + # 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: + import_location = UriLocation(import_location) + executor.submit(self._present, import_location, canonical_location, + presentation.__class__, executor) + + return _Result(presentation, canonical_location, origin_canonical_location) + + def _create_loader(self, location, origin_canonical_location): + loader = self.context.loading.loader_source.get_loader(self.context.loading, location, + origin_canonical_location) + + canonical_location = None + + if origin_canonical_location is not None: + cache_key = (origin_canonical_location, location) + try: + canonical_location = CANONICAL_LOCATION_CACHE[cache_key] + return loader, canonical_location + except KeyError: + pass + else: + cache_key = None + + canonical_location = loader.get_canonical_location() + + # Because retrieving the canonical location can be costly, we will try to cache it + if cache_key is not None: + CANONICAL_LOCATION_CACHE[cache_key] = canonical_location + + return loader, canonical_location + + def _create_presentation(self, canonical_location, loader, default_presenter_class): + # The reader we specified in the context will override + reader = self.context.reading.reader + + if reader is None: + # Read raw data from loader + reader = self.context.reading.reader_source.get_reader(self.context.reading, + canonical_location, loader) + + raw = reader.read() + + # Wrap raw data in presenter class if self.context.presentation.presenter_class is not None: - # The presenter class we specified in the context overrides everything + # The presenter class we specified in the context will override 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: + if default_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') + else: + presenter_class = default_presenter_class + + if presenter_class is None: + raise PresenterNotFoundError(u'presenter not found: {0}'.format(canonical_location)) presentation = presenter_class(raw=raw) - if presentation is not None and hasattr(presentation, '_link_locators'): + if 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() + +class _Result(object): + def __init__(self, presentation, canonical_location, origin_canonical_location): + self.presentation = presentation + self.canonical_location = canonical_location + self.origin_canonical_location = origin_canonical_location + self.merged = False + + def get_imports(self, results): + imports = [] + + def has_import(result): + for i in imports: + if i.canonical_location == result.canonical_location: + return True + return False + + for result in results: + if result.origin_canonical_location == self.canonical_location: + if not has_import(result): + imports.append(result) + return imports + + def merge(self, results, context): + # Make sure to only merge each presentation once + if self.merged: + return + self.merged = True + for result in results: + if result.presentation == self.presentation: + result.merged = True + + for result in self.get_imports(results): + # Make sure import is merged + result.merge(results, context) + + # Validate import + if hasattr(self.presentation, '_validate_import'): + if not self.presentation._validate_import(context, result.presentation): + # _validate_import will report an issue if invalid + continue + + # Merge import + if hasattr(self.presentation, '_merge_import'): --- End diff -- ditto
---