1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements. See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from ...utils.threading import FixedThreadPoolExecutor
18 from ...utils.formatting import json_dumps, yaml_dumps
19 from ..loading import UriLocation
20 from ..reading import AlreadyReadException
21 from ..presentation import PresenterNotFoundError
22 from .consumer import Consumer
27 Reads the presentation, handling imports recursively.
29 It works by consuming a data source via appropriate :class:`~aria.parser.loading.Loader`,
30 :class:`~aria.parser.reading.Reader`, and :class:`~aria.parser.presentation.Presenter`
33 It supports agnostic raw data composition for presenters that have
34 ``_get_import_locations`` and ``_merge_import``.
36 To improve performance, loaders are called asynchronously on separate threads.
38 Note that parsing may internally trigger more than one loading/reading/presentation
39 cycle, for example if the agnostic raw data has dependencies that must also be parsed.
43 if self.context.presentation.location is None:
44 self.context.validation.report('Presentation consumer: missing location')
48 imported_presentations = None
50 executor = FixedThreadPoolExecutor(size=self.context.presentation.threads,
51 timeout=self.context.presentation.timeout)
52 executor.print_exceptions = self.context.presentation.print_exceptions
54 presenter = self._present(self.context.presentation.location, None, None, executor)
58 for e in executor.exceptions:
59 self._handle_exception(e)
61 imported_presentations = executor.returns
66 if (imported_presentations is not None) and hasattr(presenter, '_merge_import'):
67 for imported_presentation in imported_presentations:
69 if hasattr(presenter, '_validate_import'):
70 okay = presenter._validate_import(self.context, imported_presentation)
72 presenter._merge_import(imported_presentation)
74 self.context.presentation.presenter = presenter
77 if self.context.has_arg_switch('yaml'):
78 indent = self.context.get_arg_value_int('indent', 2)
79 raw = self.context.presentation.presenter._raw
80 self.context.write(yaml_dumps(raw, indent=indent))
81 elif self.context.has_arg_switch('json'):
82 indent = self.context.get_arg_value_int('indent', 2)
83 raw = self.context.presentation.presenter._raw
84 self.context.write(json_dumps(raw, indent=indent))
86 self.context.presentation.presenter._dump(self.context)
88 def _handle_exception(self, e):
89 if isinstance(e, AlreadyReadException):
91 super(Read, self)._handle_exception(e)
93 def _present(self, location, origin_location, presenter_class, executor):
94 # Link the context to this thread
95 self.context.set_thread_local()
97 raw = self._read(location, origin_location)
99 if self.context.presentation.presenter_class is not None:
100 # The presenter class we specified in the context overrides everything
101 presenter_class = self.context.presentation.presenter_class
104 presenter_class = self.context.presentation.presenter_source.get_presenter(raw)
105 except PresenterNotFoundError:
106 if presenter_class is None:
108 # We'll use the presenter class we were given (from the presenter that imported us)
109 if presenter_class is None:
110 raise PresenterNotFoundError('presenter not found')
112 presentation = presenter_class(raw=raw)
114 if presentation is not None and hasattr(presentation, '_link_locators'):
115 presentation._link_locators()
117 # Submit imports to executor
118 if hasattr(presentation, '_get_import_locations'):
119 import_locations = presentation._get_import_locations(self.context)
121 for import_location in import_locations:
122 # The imports inherit the parent presenter class and use the current location as
123 # their origin location
124 import_location = UriLocation(import_location)
125 executor.submit(self._present, import_location, location, presenter_class,
130 def _read(self, location, origin_location):
131 if self.context.reading.reader is not None:
132 return self.context.reading.reader.read()
133 loader = self.context.loading.loader_source.get_loader(self.context.loading, location,
135 reader = self.context.reading.reader_source.get_reader(self.context.reading, location,